cocos渲染引擎分析(六)-----shader渲染流程之内置shader构建

u单纯使用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());
            }
    p->link();
    p->updateUniforms();

}
//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;
}

其中compileShader中会根据平台信息等,向shader中加入版本号的定义headersDef,以及一些cocos内置的Uniform变量如CC_PMatrix, CC_AmbientColor等,最终编译代码如下:

bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source, const std::string& compileTimeHeaders, const std::string& convertedDefines)
{
    GLint status;

    if (!source)
    {
        return false;
    }

    std::string headersDef;//版本信息定义
    if (compileTimeHeaders.empty()) {
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
        headersDef = "#version 100\n precision mediump float;\n precision mediump int;\n";
    }else{
        headersDef = compileTimeHeaders;
    }
    const GLchar *sources[] = {
        headersDef.c_str(),//版本信息定义
        COCOS2D_SHADER_UNIFORMS,//cocos内置变量
        convertedDefines.c_str(),
        source};

    *shader = glCreateShader(type);
    glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);
    glCompileShader(*shader);

    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);

    if (! status)
    {
       
        return false;
    }

    return (status == GL_TRUE);
}

创建完成后,GLProgram调用链接和更新Uniform如下:

bool GLProgram::link()
{
    bindPredefinedVertexAttribs();//绑定预定义属性,每个模型的属性是不同的,使用时会重新绑定
    //链接Program和ver,frag
    glLinkProgram(_program);
    glGetProgramiv(_program, GL_LINK_STATUS, &status);

    if (status == GL_FALSE)
    {
        GL::deleteProgram(_program);
        _program = 0;
    }
    else
    {
        //解析获取shader中的顶点属性和Uniform属性,后面传值用
        parseVertexAttribs();
        parseUniforms();
        //删除vert和frag句柄
        clearShader();
    }

    return (status == GL_TRUE);
}
   

其中解析顶点和Uniform属性的函数如下,cocos使用_vertexAttribs和_userUniforms记录shader的属性变量和uniform变量,具体模型使用此program传值时,会根据模型的属性的name查询出对应location,如果此shader中没有此属性,则不传,parseVertexAttribs的函数如下(parseUniforms类似):

void GLProgram::parseVertexAttribs()
{
    GLint activeAttributes;
    GLint length;
    glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
    if(activeAttributes > 0)
    {
        VertexAttrib attribute;
        glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &length);
        if(length > 0)
        {
            GLchar* attribName = (GLchar*) alloca(length + 1);
            for(int i = 0; i < activeAttributes; ++i)
            {
                // 记录顶点属性的名字和locatio,
                glGetActiveAttrib(_program, i, length, nullptr, &attribute.size, &attribute.type, attribName);
                attribName[length] = '\0';
                attribute.name = std::string(attribName);
                attribute.index = glGetAttribLocation(_program, attribName);
                _vertexAttribs[attribute.name] = attribute;
            }
        }
    }

获取内置shader的信息,使用updateUniforms()获取shader的内置信息

void GLProgram::updateUniforms()
{    
     _builtInUniforms[UNIFORM_AMBIENT_COLOR] = glGetUniformLocation(_program, UNIFORM_NAME_AMBIENT_COLOR);
    _builtInUniforms[UNIFORM_P_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_P_MATRIX);
    _builtInUniforms[UNIFORM_MULTIVIEW_P_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MULTIVIEW_P_MATRIX);
    _builtInUniforms[UNIFORM_MV_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MV_MATRIX);
    _builtInUniforms[UNIFORM_MVP_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MVP_MATRIX);
    _builtInUniforms[UNIFORM_MULTIVIEW_MVP_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MULTIVIEW_MVP_MATRIX);

   _flags.usesP = _builtInUniforms[UNIFORM_P_MATRIX] != -1;//是否使用投影矩阵变量
    _flags.usesMultiViewP = _builtInUniforms[UNIFORM_MULTIVIEW_P_MATRIX] != -1;
    _flags.usesMV = _builtInUniforms[UNIFORM_MV_MATRIX] != -1;
    _flags.usesMVP = _builtInUniforms[UNIFORM_MVP_MATRIX] != -1;

 this->use();//调用 glUseProgram(program);
   
}

至此,完成了所有内置shader编译连接,使用时,不同材质使用不同的GLProgram,然后传递属性参数。

发布了55 篇原创文章 · 获赞 12 · 访问量 8万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览