文件结构
cocos2d-x 与 shader 相关的代码在 renderer 目录下
|-cocos
|-renderer
|-CCGLProgram.h
|-CCGLProgram.cpp
|-CCGLProgramCache.h
|-CCGLProgramCache.cpp
|-CCGLProgramState.h
|-CCGLProgramState.cpp
|-CCGLProgramStateCache.h
|-CCGLProgramStateCache.cpp
|-...
|-ccShader_PositionColor.vert
|-ccShaer_PositionColor.frag
|-ccShader_UI_Gray.frag
|-...
其中比较重要的几个类是 GLProgram,GLProgramCache,GLProgramState 和 GLProgramStateCache;除此之外还定义一些默认的着色器程序,.vert 后缀的文件是顶点着色器程序,.frag 后缀的文件是片段着色器程序。下面通过解析这几个主要类了解着色器的创建过程和使用方法
GLProgramCache
首先从 GLProgramCache 这个类入手,这个类是着色器 Program 的一个缓冲区,也就是说这个类保存了创建好的着色器程序,使用的时候直接从这缓冲中取 Program 即可。和其它缓冲类一样,GLProgramCache 也是单例模式
GLProgramCache* GLProgramCache::getInstance()
{
if (!_sharedGLProgramCache) {
_sharedGLProgramCache = new (std::nothrow) GLProgramCache();
if (!_sharedGLProgramCache->init())
{
CC_SAFE_DELETE(_sharedGLProgramCache);
}
}
return _sharedGLProgramCache;
}
第一次调用 getInstance 方法会先创建一个 GLProgramCache 实例,然后调用 init 方法进行初始化
bool GLProgramCache::init()
{
loadDefaultGLPrograms();
auto listener = EventListenerCustom::create(Configuration::CONFIG_FILE_LOADED, [this](EventCustom* event){
reloadDefaultGLProgramsRelativeToLights();
});
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener, -1);
return true;
}
init 方法一个很重要的功能就是调用 loadDefaultGLPrograms 方法,这个方法创建所有默认的着色器程序
void GLProgramCache::loadDefaultGLPrograms()
{
// Position Texture Color shader
GLProgram *p = new (std::nothrow) GLProgram();
loadDefaultGLProgram(p, kShaderType_PositionTextureColor);
_programs.insert( std::make_pair( GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR, p ) );
// Position Texture Color without MVP shader
p = new (std::nothrow) GLProgram();
loadDefaultGLProgram(p, kShaderType_PositionTextureColor_noMVP);
_programs.insert( std::make_pair( GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, p ) );
//...
}
loadDefaultGLPrograms 创建 cocos 自带的所有着色器程序,首先创建一个 GLProgram,然后调用 loadDefaultGLProgram 进行内容填充,再插入到 _programs 字典中去
void GLProgramCache::loadDefaultGLProgram(GLProgram *p, int type)
{
switch (type) {
case kShaderType_PositionTextureColor:
p->initWithByteArrays(ccPositionTextureColor_vert, ccPositionTextureColor_frag);
break;
case kShaderType_PositionTextureColor_noMVP:
//...
//...
default:
CCLOG("cocos2d: %s:%d, error shader type", __FUNCTION__, __LINE__);
return;
}
p->link();
p->updateUniforms();
CHECK_GL_ERROR_DEBUG();
}
loadDefaultGLProgram 用于创建单个着色器程序,它接收两个参数,一个是事先创建出来的 GLProgram,一个是着色器类型;然后调用 GLProgram 的 initWithByteArrays 进行内容填充,根据不同的 type 传递不同的着色器源文件,ccPositionTextureColor_vert 这些变量在着色器源文件中定义,即上面说到的 .vert 和 .frag 文件中定义,它们的值是一个字符串,也就是着色器的源代码。这里为什么可以直接使用 ccPositionTextureColor_vert 这些变量呢?这是因为这些变量都是全局变量,而且在 ccShaders.cpp 中添加了所有的引用,ccShaders.cpp 的内容如下
#include "renderer/ccShaders.h"
#d