关于与GLSL Shader特效,有两个比较重要的问题,下边分别讲一下:
1、如何管理自定义的shader?
2、如何解决Android上游戏从后台切换回来时,自定义shader不能自动加载?
下面针对2个问题来解答:
Cocos2d-x从2.2-3.0,对于shader部分基本上没有做很多的变化,而在引擎中使用自定义的shader相对而言是比较麻烦的,尤其是在Android上。你会发现app从后台切换回来的时候,自定义的shader没有被加载。而为了解决这个问题你不得不修改引擎的内部代码。这是很不好的习惯。【在3.2以后版本,提供了GLProgramState来解决】
下面先分析下,Cocos2d-x shader的框架:(Cocos2d-x 3.0 Android)
引擎内置的shader program由一个全局的ShaderCache初始化并持有。
初始化:
getInstance-->init()--->loadDefaultShaders();
释放:
Director::purgeDirector-->ShaderCache::destroyInstance();
后台返回(Javaactivity.cpp):
cocos2d::ShaderCache::getInstance()->reloadDefaultGLPrograms();
(在3.2的版本中,ShaderCache的名字改为了ProgramCache。其他部分基本没有变化。)
基于上述框架,如果用户在Android上添加自己shader,就不得不在Javaactivity.cpp中添加自己的reload代码。这反映了目前引擎的shader部分存在不足。本人认为shader部分应该被重写,向开发者提供一个custom的接口。
先看下opengl中,使用shader的流程:
1. 编译vertex shader或者fragment shader;
2. 链接shader;
3. 初始化需要向shader传递的变量;(如:getUniformLocation)
4. 在render函数中,实时传递shader变量。(如:setUniformLocation)
问题症结在第三步,对于不同的shader,需要初始化的shader变量不同,也很难预测开发者需要向shader传递哪些变量。
解决这个问题的方法是:
抽象一个shaderNode父类,完成shader的前面2个步骤,并提供一个抽象接口(如:virtual void buildCustomUniforms() = 0;)来完成第三步的初始化,让所有继承的子类来实现这个接口。
举例:
ShaderNode:所有shader的父类,提供公共接口的实现,如shader的编译和链接,shader从后台切换回来时reload函数的实现。
CustomShader:开发者自定义的shader,继承ShaderNode,并实现抽象接口buildCustomUniforms。在buildCustomUniforms中初始化自定义的shader变量。
ShaderCache:全局shader cache的持有者,使用shader_dec来描述所持有的ShaderNode。
shader_dec:ShaderNode的描述结构体,包括vertex shader和fragment shader的文件名,查找key和ShaderNode指针(回调具体ShaderNode的reload函数)。
如图:
源代码见文件。
使用方法:
1. 定义需要加载的shader table:
static cocos2d::shaderDes shader_tbl[] = {
{shader_comm_vs, shader_a_fs, “a”, NULL},
{shader_comm_vs, shader_b_fs, "b", NULL}
{shader_comm_vs, shader_c_fs, "c", NULL}
};
2. 在预加载页面(如:loading scene):
for (int i = 0; i < ARRAYSIZE(shader_tbl); i++) {
CustomShaderCache::getInstance()->addShader(shader_tbl[i]);
}
3. 使用shader:
xxxShader::create(shader_key); /* @shader_key是自定义的shader table中的key。*/
说明:
附件的源代码没有修改引擎的shader cache的实现模块,是另外实现的CustomShaderCache ,并添加在Cocos2d-x-3.0\cocos\2d”下,作为libcocos2d编译进引擎中。当然,也可以重写引擎的shadercache模块。 CustomShaderCache的实现和引擎的shadercache模块基本相似,只是 CustomShaderCache向外提供了一个自定义shader的类。这样自定义的shader只需要继承ShaderNode即可。其他的reload等工作由CustomShaderCache实现。
需要修改的地方: