cocos2dx中的shader由GLProgram创建,一共有3个步骤
1.初始化顶点着色器和像素着色器
2.链接着色程序
3.更新uniform变量信息
GLProgram* GLProgram::createWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeDefines)
{
auto ret = new (std::nothrow) GLProgram();
/*编译着色器并关联到着色程序上*/
if(ret && ret->initWithByteArrays(vShaderByteArray, fShaderByteArray, compileTimeDefines)) {
/*链接着色程序*/
ret->link();
/*记录uniform的位置,和使用了哪些uniform*/
ret->updateUniforms();
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
/编译顶点着色器和像素着色器/
bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeDefines)
{
/*创建着色程序,顶点着色器和像素着色器需要关联到这个着色程序后完成链接*/
_program = glCreateProgram();
CHECK_GL_ERROR_DEBUG();
// convert defines here. If we do it in "compileShader" we will do it it twice.
// a cache for the defines could be useful, but seems like overkill at this point
std::string replacedDefines = "";
replaceDefines(compileTimeDefines, replacedDefines);
_vertShader = _fragShader = 0;
/*编译顶点着色器*/
if (vShaderByteArray)
{
if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray, replacedDefines))
{
CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
return false;
}
}
/*编译像素着色器*/
// Create and compile fragment shader
if (fShaderByteArray)
{
if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray, replacedDefines))
{
CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
return false;
}
}
/*关联到着色程序上*/
if (_vertShader)
{
glAttachShader(_program, _vertShader);
}
CHECK_GL_ERROR_DEBUG();
if (_fragShader)
{
glAttachShader(_program, _fragShader);
}
_hashForUniforms.clear();
CHECK_GL_ERROR_DEBUG();
return true;
}
顶点着色器和像素着色器都需要使用这个函数完成编译
bool GLProgram::compileShader(GLuint* shader, GLenum type, const GLchar* source, const std::string& convertedDefines)
{
GLint status;
if (!source)
{
return false;
}
/*在shander头添加额外信息,比如精度控制,还有cocos2dx的uniform变量的定义,这也是shader中有些uniform变量没有定义就可以使用的原因。
static const char * COCOS2D_SHADER_UNIFORMS =
"uniform mat4 CC_PMatrix;\n"
"uniform mat4 CC_MVMatrix;\n"
"uniform mat4 CC_MVPMatrix;\n"
"uniform mat3 CC_NormalMatrix;\n"
"uniform vec4 CC_Time;\n"
"uniform vec4 CC_SinTime;\n"
"uniform vec4 CC_CosTime;\n"
"uniform vec4 CC_Random01;\n"
"uniform sampler2D CC_Texture0;\n"
"uniform sampler2D CC_Texture1;\n"
"uniform sampler2D CC_Texture2;\n"
"uniform sampler2D CC_Texture3;\n"
"//CC INCLUDES END\n\n";
*/
const GLchar *sources[] = {
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
(type == GL_VERTEX_SHADER ? "precision mediump float;\n precision mediump int;\n" : "precision mediump float;\n precision mediump int;\n"),
#elif (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
(type == GL_VERTEX_SHADER ? "precision highp float;\n precision highp int;\n" : "precision mediump float;\n precision mediump int;\n"),
#endif
COCOS2D_SHADER_UNIFORMS,
convertedDefines.c_str(),
source};
/*创建并编译shader对象*/
*shader = glCreateShader(type);
glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);
glCompileShader(*shader);
/*检查编译结果*/
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
if (! status)
{
/*编译失败时的信息提示*/
GLsizei length;
glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);
GLchar* src = (GLchar *)malloc(sizeof(GLchar) * length);
glGetShaderSource(*shader, length, nullptr, src);
CCLOG("cocos2d: ERROR: Failed to compile shader:\n%s", src);
if (type == GL_VERTEX_SHADER)
{
CCLOG("cocos2d: %s", getVertexShaderLog().c_str());
}
else
{
CCLOG("cocos2d: %s", getFragmentShaderLog().c_str());
}
free(src);
return false;;
}
return (status == GL_TRUE);
}
像素着色器和顶点着色器编译成功并且关联到着色程序后,使用link完成链接
bool GLProgram::link()
{
CCASSERT(_program != 0, "Cannot link invalid program");
GLint status = GL_TRUE;
/*绑定cocos2dx的顶点属性到固定的索引上*/
bindPredefinedVertexAttribs();
/*链接着色程序*/
glLinkProgram(_program);
/*把活跃的顶点属性,也就是着色程序中使用到的顶点属性存储到_vertexAttribs中*/
parseVertexAttribs();
/*把cocos2dx不能识别的uniform变量存到_userUniforms中*/
parseUniforms();
/*释放不需要的着色器资源*/
clearShader();
#if DEBUG || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
glGetProgramiv(_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
CCLOG("cocos2d: ERROR: Failed to link program: %i", _program);
GL::deleteProgram(_program);
_program = 0;
}
#endif
return (status == GL_TRUE);
}
绑定cocos2dx的顶点属性到固定的索引上
void GLProgram::bindPredefinedVertexAttribs()
{
/*cocos2dx定义的顶点属性*/
static const struct {
const char *attributeName;
int location;
} attribute_locations[] =
{
{GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION},
{GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3},
{GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL},
};
const int size = sizeof(attribute_locations) / sizeof(attribute_locations[0]);
/*绑定到固定的顶点属性索引*/
for(int i=0; i<size;i++) {
glBindAttribLocation(_program, attribute_locations[i].location, attribute_locations[i].attributeName);
}
}
记录uniform的位置,和使用了哪些uniform
void GLProgram::updateUniforms()
{
/*记录uniform的位置,设置uniform变量时,需要这个位置信息*/
_builtInUniforms[UNIFORM_AMBIENT_COLOR] = glGetUniformLocation(_program, UNIFORM_NAME_AMBIENT_COLOR);
_builtInUniforms[UNIFORM_P_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_P_MATRIX);
_builtInUniforms[UNIFORM_MV_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MV_MATRIX);
_builtInUniforms[UNIFORM_MVP_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MVP_MATRIX);
_builtInUniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_NORMAL_MATRIX);
_builtInUniforms[UNIFORM_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_TIME);
_builtInUniforms[UNIFORM_SIN_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_SIN_TIME);
_builtInUniforms[UNIFORM_COS_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_COS_TIME);
_builtInUniforms[UNIFORM_RANDOM01] = glGetUniformLocation(_program, UNIFORM_NAME_RANDOM01);
_builtInUniforms[UNIFORM_SAMPLER0] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER0);
_builtInUniforms[UNIFORM_SAMPLER1] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER1);
_builtInUniforms[UNIFORM_SAMPLER2] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER2);
_builtInUniforms[UNIFORM_SAMPLER3] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER3);
/*记录使用了哪些可识别的uniform,根据这个信息做优化*/
_flags.usesP = _builtInUniforms[UNIFORM_P_MATRIX] != -1;
_flags.usesMV = _builtInUniforms[UNIFORM_MV_MATRIX] != -1;
_flags.usesMVP = _builtInUniforms[UNIFORM_MVP_MATRIX] != -1;
_flags.usesNormal = _builtInUniforms[UNIFORM_NORMAL_MATRIX] != -1;
_flags.usesTime = (
_builtInUniforms[UNIFORM_TIME] != -1 ||
_builtInUniforms[UNIFORM_SIN_TIME] != -1 ||
_builtInUniforms[UNIFORM_COS_TIME] != -1
);
_flags.usesRandom = _builtInUniforms[UNIFORM_RANDOM01] != -1;
this->use();
/*绑定纹理采样*/
// Since sample most probably won't change, set it to 0,1,2,3 now.
if(_builtInUniforms[UNIFORM_SAMPLER0] != -1)
setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER0], 0);
if(_builtInUniforms[UNIFORM_SAMPLER1] != -1)
setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER1], 1);
if(_builtInUniforms[UNIFORM_SAMPLER2] != -1)
setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER2], 2);
if(_builtInUniforms[UNIFORM_SAMPLER3] != -1)
setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER3], 3);
}