Irrlicht学习备忘录——10 Shaders

10 Shaders

        官方代码($sdk)\examples\10.Shaders

Irrlicht学习备忘录——10 Shaders_第1张图片

       这个例子是在irr里使用DXOpenGLShader,使用Shader创建新的材质类型。

       同固定管线渲染一样,使用Shader也需要用到很多矩阵,但是这些矩阵信息不再是irr自动提供,而是通过继承IShaderConstantSetCallBack接口类提供矩阵信息给Shader。在该例中使用了一些简单的顶点Shader,它会根据摄像机的位置计算顶点的颜色。实现这功能,需要为Shader提供转换法线的逆世界矩阵、转换坐标的剪切矩阵、摄像机位置和物体在世界矩阵中的位置。这些数据将在自己继承的IShaderConstantSetCallBack接口类的OnSetConstanes()方法中使用。OnSetConstanes()会在每次设置材质的时候被调用。它的参数IMaterialRendererServices接口类的setVertexShaderConstant()方法用来设置Shader所需的数据。下面看如何继承IShaderConstantSetCallBack接口类。

classMyShaderCallBack : public video::IShaderConstantSetCallBack

{

public:

//参数services:指向一个接口,它提供设置Shader常量的方法

//参数userData:创建Shader时能指定用户数据

virtualvoid OnSetConstants(video::IMaterialRendererServices* services,

s32userData)

{

video::IVideoDriver*driver = services->getVideoDriver();

//设置逆向世界矩阵

//如果使用高级Shader(用户可以在程序开始时进行选择),在此必须设置其名

称。

core::matrix4invWorld = driver->getTransform(video::ETS_WORLD);

invWorld.makeInverse();

if(UseHighLevelShaders)

services->setVertexShaderConstant("mInvWorld",invWorld.pointer(), 16);

else

services->setVertexShaderConstant(invWorld.pointer(),0, 4);

//设置剪切矩阵

core::matrix4worldViewProj;

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);

//设置摄像机位置

core::vector3dfpos = device->getSceneManager()->

getActiveCamera()->getAbsolutePosition();

if(UseHighLevelShaders)

services->setVertexShaderConstant("mLightPos",reinterpret_cast<f32*>(&pos), 3);

else

services->setVertexShaderConstant(reinterpret_cast<f32*>(&pos),8, 1);

//设置灯光颜色

video::SColorfcol(0.0f,1.0f,1.0f,0.0f);

if(UseHighLevelShaders)

services->setVertexShaderConstant("mLightColor",

reinterpret_cast<f32*>(&col),4);

else

services->setVertexShaderConstant(reinterpret_cast<f32*>(&col),9, 1);

//设置世界转换矩阵

core::matrix4world = driver->getTransform(video::ETS_WORLD);

world= world.getTransposed();

if(UseHighLevelShaders)

{

services->setVertexShaderConstant("mTransWorld",world.pointer(), 16);

//设置纹理,它可以同时使用intfloat类型的setPixelShaderConstant接口(只有使用OpenGL的时候需要它)

s32TextureLayerID = 0;

if(UseHighLevelShaders)

services->setPixelShaderConstant("myTexture",&TextureLayerID, 1);

}

else

services->setVertexShaderConstant(world.pointer(),10, 4);

}

};

       这个例子启动时除了3D驱动选择菜单还比以往多增加了一个选择菜单,用于选择是否使用高级Shader和是否使用Cg。新增加的菜单代码如下

if(driverType == video::EDT_DIRECT3D9 ||

driverType == video::EDT_OPENGL)

{

chari;

printf("Pleasepress 'y' if you want to use high level shaders.\n");

std::cin>> i;

if(i == 'y')

{

UseHighLevelShaders= true;

printf("Pleasepress 'y' if you want to use Cg shaders.\n");

std::cin>> i;

if(i == 'y')

UseCgShaders= true;

}

}

确认是否支持Cg

if(UseCgShaders && !driver->queryFeature(video::EVDF_CG))

{

printf("Warning:No Cg support, disabling.\n");

UseCgShaders=false;

}

       似乎从CUDA出来后Cg就不再热了。不管怎么说,irr引擎支持是好事。

       下面设置使用的Shader文件名。如果使用DX,需要使用顶点着色器和像素着色器程序,如果使用OpenGL,则需要使用顶点着色器和ARB片段着色器程序。在SDKmedia文件夹下,已经有为不同驱动设备写好的相应文件,分别是d3d8.ps,d3d8.vs, d3d9.ps, d3d9.vs,opengl.ps and opengl.vs

switch(driverType)

{

//设置DX8对应的Shader文件

casevideo::EDT_DIRECT3D8:

psFileName= "../../media/d3d8.psh";

vsFileName= "../../media/d3d8.vsh";

break;

//设置DX9对应的Shader文件

casevideo::EDT_DIRECT3D9:

if(UseHighLevelShaders)

{

//Cg能处理HLSL

psFileName= "../../media/d3d9.hlsl";

vsFileName= psFileName; // 两种Shader在相同文件里

}

else

{

psFileName= "../../media/d3d9.psh";

vsFileName= "../../media/d3d9.vsh";

}

break;

//设置OpenGL对应的Shader文件

casevideo::EDT_OPENGL:

if(UseHighLevelShaders)

{

if(!UseCgShaders)

{

psFileName= "../../media/opengl.frag";

vsFileName= "../../media/opengl.vert";

}

else

{

//Cg使用HLSL psFileName= "../../media/d3d9.hlsl";

vsFileName= psFileName; // both shaders are in the same file

}

}

else

{

psFileName= "../../media/opengl.psh";

vsFileName= "../../media/opengl.vsh";

}

break;

}

       检测硬件是否支持相应的Shader,如果不支持,就将Shader文件名设为空,后面就不会再进行该类Shader显示。

if(!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&

!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))

{

device->getLogger()->log("WARNING:Pixel shaders disabled "\

"becauseof missing driver/hardware support.");

psFileName= "";

}


if(!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&

!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))

{

device->getLogger()->log("WARNING:Vertex shaders disabled "\

"becauseof missing driver/hardware support.");

vsFileName= "";

}

       在irr中改变一个材质,可以通过修改材质结构中的材质类型值(Video::EMT_SOLID)来达到相应的效果,这个值是一个32位数值。要创建一个新材质,就需要irr来创建一个新的材质类型值,然后用它做参数进行修改就行了。接下来就是创建新的Shader材质。先通过irr驱动获得IGPUProgrammingServices指针,通过这个指针的addShaderMaterialFromFiles()方法获得一个新的材质类型值。s32addShaderMaterialFromFiles(const io::path&vertexShaderProgramFileName, const io::path&pixelShaderProgramFileName,IShaderConstantSetCallBack* callback =0,E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,s32 userData = 0),第一个参数是顶点Shader的文件,第二个参数是像素Shader的文件,第三个参数IShaderConstantSetCallBack类的指针,本例一开始写的那个MyShaderCallBack类就是继承它,如果不想设置,这里也可设为空指针0。第四个参数是告诉irr哪个材质类型时候作为基层材质。第四个参数是个用户自定义参数,该方法调用IShaderConstantSetCallBack类的OnSetConstants时会把这个参数传进去。返回值就是新创建的材质类型值。如果创建失败会返回-1

//获取IGPUProgrammingServices指针

video::IGPUProgrammingServices*gpu = driver->getGPUProgrammingServices();

//例子里为了说明第四个参数的用途,创建了两个不同基层材质类型的材质,一个使用EMT_SOLID (不透明的)另一个使用EMT_TRANSPARENT_ADD_COLOR(透明+颜色)。

s32newMaterialType1 = 0;

s32newMaterialType2 = 0;

if(gpu)

{

//创建自己写的Shader回调函数类

MyShaderCallBack*mc = new MyShaderCallBack();

//根据用户选择创建高级Shader或低级Shader

if(UseHighLevelShaders)

{

//创建高级Shader

constvideo::E_GPU_SHADING_LANGUAGE shadingLanguage =UseCgShaders ?video::EGSL_CG:video::EGSL_DEFAULT;

//hlslglslcg创建高级Shader材质

newMaterialType1= gpu->addHighLevelShaderMaterialFromFiles(

vsFileName,"vertexMain", video::EVST_VS_1_1,

psFileName,"pixelMain", video::EPST_PS_1_1,

mc,video::EMT_SOLID, 0, shadingLanguage);

newMaterialType2= gpu->addHighLevelShaderMaterialFromFiles(

vsFileName,"vertexMain", video::EVST_VS_1_1,

psFileName,"pixelMain", video::EPST_PS_1_1,

mc,video::EMT_TRANSPARENT_ADD_COLOR, 0 , shadingLanguage);

}

else

{

//asm arb_asm创建低级Shader材质

newMaterialType1= gpu->addShaderMaterialFromFiles(vsFileName,

psFileName,mc, video::EMT_SOLID);

newMaterialType2= gpu->addShaderMaterialFromFiles(vsFileName,

psFileName,mc, video::EMT_TRANSPARENT_ADD_COLOR);

}

mc->drop();

}

       接下来例子中的代码是前面其他例子里经常出现的内容。创建测试用的立方体,并把刚创建的材质设置上去。为了好区别不同立方体用的材质类型,在立方体上添加了文字场景节点。为使这些立方体看起来更加有趣,同时更容易看出不同材质的区别,在立方体上加了一个旋转器。设置材质使用的是场景节点的setMaterialType方法,参数就是前面创建的材质类型值newMaterialType1newMaterialType2。但因setMaterialType接受的参数必须是video::E_MATERIAL_TYPE枚举类型,这里需要进行强制类型转换。

       一共创建了3个立方体,第一个使用的是newMaterialType1材质,它使用了顶点Shader和像素Shader,基础材质类型是不透明;第二个使用的是newMaterialType2材质,它使用了顶点Shader和像素Shader,基础材质类型是透明+颜色;第三个没有使用Shader材质,是默认材质。

       后面为了使场景更好看,创建了天空盒。但在这里多出了一句driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,false),这句在前面的例子里从来没有出现过,从方法名setTextureCreationFlag能知道它是设置irr驱动创建纹理的方法的,会opengldx的,一看到参数中的MIP_MAPS就知道这句是干什么用的了。这句是关闭了Mipmap纹理创建。简单的说,Mipmap纹理就是从大到小不同规格的同一图像,每一个规格,刚好是上一个规格的一半。当这个图像离用户距离近的时候,就是用较大的图像给用户看;当图像离用户远的时候,就是使用较小的图像给用户看。这样做能够减少每一帧对纹理进行缩放的计算量,同时如果各个规格的纹理是专门处理过的,在显示时看到的效果将比直接缩放效果要好的多。因天空盒不需要使用Mipmap,这里就关闭了这个纹理创建标志。

       最后的代码更没什么好说的了,就是那么再熟悉不过的帧循环。

       在这个例子中,所有Shader都是写到文件中的,其实也可以把Shader写到cpp文件中,但这是就不是使用IGPUProgrammingServices中的addShaderMaterialFromFiles(),而是使用

addShaderMaterial(),填写的参数也不再是文件名,而是Shader函数代码的函数名。想要更详细的了解irr的材质,应该仔细看看SMaterial.h头文件。

你可能感兴趣的:(学习,游戏引擎,游戏编程,Irrlicht,3D编程)