单纯使用OPENGl写一个场景渲染时,可以很容易的看出OEPNGL整个渲染流程,cocos引擎将整个渲染流程进行了封装分类,各司其职。虽然很多人说cocos渲染底层是学生写的,不少问题,然而cocos这个小巧的引擎,确是一个很好的学习引擎渲染架构的方式,本篇主要讲,引擎底层怎么组织shader,读取模型,材质,创建生成渲染指令的过程进行剖析,本章记录内置shader的构建过程。
内置shader和材质构建:
首先,程序创建scene后,会创建一个默认相机,相机创建过程中,会创建一个CameraBackgroundBrush,用于刷黑屏幕,代码如下:
Camera::Camera()
{
_clearBrush = CameraBackgroundBrush::createDepthBrush(1.f);
_clearBrush->retain();
}
CameraBackgroundBrush创建过程会调用下面的函数:
bool CameraBackgroundDepthBrush::init()
{
//获取擦除相机的shader
auto shader = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_CAMERA_CLEAR);
//创建ProgramState,具体而言就是材质,shader的具体化
_glProgramState = GLProgramState::getOrCreateWithGLProgram(shader);
_glProgramState->retain();
。。。。。。。
//绘制矩形背景
。。。。。。。。
return true;
}
其中,程序CameraBackgroundBrush初始过程中会调用GLProgramCache::getInstance(),如果GLProgramCache( 内置shader的集合)不存在,就创建,并返回,单例模式,运行过程中只有一个。
GLProgramCache* GLProgramCache::getInstance()
{
if (!_sharedGLProgramCache) {
_sharedGLProgramCache = new (std::nothrow) GLProgramCache();
if (!_sharedGLProgramCache->init())
{
CC_SAFE_DELETE(_sharedGLProgramCache);
}
}
return _sharedGLProgramCache;
}
GLProgramCache的初始化函数如下:
bool GLProgramCache::init()
{
loadDefaultGLPrograms();
}
void GLProgramCache::loadDefaultGLPrograms()
{
//加载各种材质类型的shader
GLProgram *p = new (std::nothrow) GLProgram();
loadDefaultGLProgram(p, kShaderType_PositionTextureColor);
_programs.emplace(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR, p);
............
p = new (std::nothrow) GLProgram();
loadDefaultGLProgram(p, kShaderType_3DPositionNormalTex);
_programs.emplace(GLProgram::SHADER_3D_POSITION_NORMAL_TEXTURE, p);
}
GLProgram指可以附加着色器对象的对象,具体而言可以理解为一个材质对象,用于的顶点着色器和面片着色器shader的编译,链接和顶点属性,uniform参数获取,后面使用时,使用GLProgram类向shader中传递顶点变量和Uniform变量。下面以kShaderType_3DPositionNormalTex(3D法线纹理材质)为例,记录GLProgram的创建过程:
void GLProgramCache::loadDefaultGLProgram(GLProgram *p, int type)
{
case kShaderType_3DPositionNormalTex://根据特定类别调用特定的加载特定的.ver和.frag文件
{
std::string def = getShaderMacrosForLight();
p->initWithByteArrays((def +
std::string(cc3D_PositionNormalTex_vert)).c_str(), (def +
std::string(cc3D_ColorNormalTex_frag)).c_str());
}
}
//GLProgram的创建过程如下
bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeHeaders, const std::string& compileTimeDefines)
{
_program = glCreateProgram();//创建一个空program
//预定义开关,用于编译shader时,静态编译部分shader(#if #else),不同if和else
std::string replacedDefines = "";
useRealTimeBRDF = replaceDefines(compileTimeDefines, replacedDefines);
_vertShader = _fragShader = 0;
//创建顶点着色器
if (vShaderByteArray)
if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray..))
return false;
// 创建面片着色器
if (fShaderByteArray)
if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER ...))
return false;
//绑定顶点着色器到当前program
if (_vertShader)
glAttachShader(_program, _vertShader);
if (_fragShader)
glAttachShader(_program, _fragShader);
//清空Uniform变量
clearHashUniforms();
return true;
}
至此,完成了所有内置shader编译连接,使用时,不同材质使用不同的GLProgram,然后传递属性参数。