Cocos2d-x 3.x 图形学渲染系列十六

来源:互联网 发布:特效视频软件app 编辑:程序博客网 时间:2024/05/01 07:30

笔者介绍:姜雪伟IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

每个引擎都有自己的处理Shader类,Cocos使用的是GLProgram类,之所以定义GLProgram类,是因为在引擎中需要有一个类管理模型的信息和矩阵信息声明。在GLProgram类中定义了模型顶点的属性,这些属性在加载模型时,用于解释模型文件内容时用于做属性标记处理,它是使用枚举定义的,枚举类代码如下所示:

    enum    {/**索引0用于定义位置*/        VERTEX_ATTRIB_POSITION,/**索引1用于定义颜色*/        VERTEX_ATTRIB_COLOR,/**索引2用于定义纹理坐标单元0*/        VERTEX_ATTRIB_TEX_COORD,/**索引3用于定义纹理坐标单元1.*/        VERTEX_ATTRIB_TEX_COORD1,/**索引4用于定义纹理坐标单元2.*/        VERTEX_ATTRIB_TEX_COORD2,/**索引5用于定义纹理坐标单元3.*/        VERTEX_ATTRIB_TEX_COORD3,/**索引6用于定义法线*/        VERTEX_ATTRIB_NORMAL,/**索引7用于定义混合权重*/        VERTEX_ATTRIB_BLEND_WEIGHT,/**索引8用于定义混合索引.*/        VERTEX_ATTRIB_BLEND_INDEX,/**索引9用于定义正切.*/        VERTEX_ATTRIB_TANGENT,/**索引10用于定义次法线.*/        VERTEX_ATTRIB_BINORMAL,        VERTEX_ATTRIB_MAX,        VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD,    };

在后面有关模型章节时,会有这方面的实际操作,接下来GLProgram类还定义了Shader中经常用于计算矩阵转换的标记,也是以枚举形式定义的如下所示:

    enum    {/**环境颜色.*/        UNIFORM_AMBIENT_COLOR,/**投影矩阵*/        UNIFORM_P_MATRIX,/**模型视口矩阵.*/        UNIFORM_MV_MATRIX,/**模型视口投影矩阵.*/        UNIFORM_MVP_MATRIX,/**法线矩阵.*/        UNIFORM_NORMAL_MATRIX,/**时间.*/        UNIFORM_TIME,/**sin(时间).*/        UNIFORM_SIN_TIME,/**cos(时间).*/        UNIFORM_COS_TIME,/**随机数字.*/        UNIFORM_RANDOM01,/** @{        * 对纹理的取样0-3        */        UNIFORM_SAMPLER0,        UNIFORM_SAMPLER1,        UNIFORM_SAMPLER2,        UNIFORM_SAMPLER3,/**@}*/        UNIFORM_MAX,};


GLProgram类不只是只定义枚举属性,它还提供了加载顶点着色器和片段着色器接口函数,通过这些接口开发者可以知道它在加载Shader脚本时是如何解释其内容的。加载Shader脚本函数如下所示:

GLProgram* GLProgram::createWithFilenames(const std::string& vShaderFilename, const std::string& fShaderFilename, const std::string& compileTimeDefines){auto ret = new (std::nothrow) GLProgram();if(ret && ret->initWithFilenames(vShaderFilename, fShaderFilename, compileTimeDefines)) {        ret->link();        ret->updateUniforms();        ret->autorelease();return ret;    }CC_SAFE_DELETE(ret);return nullptr;}

createWithFilenames函数中的参数是顶点着色器和片段着色器的文件路径,函数内部调用了initWithFilenames函数,继续深入进去查看该函数执行细节,内容如下所示:

bool GLProgram::initWithFilenames(const std::string& vShaderFilename, const std::string& fShaderFilename, const std::string& compileTimeDefines){auto fileUtils = FileUtils::getInstance();std::string vertexSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(vShaderFilename));std::string fragmentSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(fShaderFilename));returninitWithByteArrays(vertexSource.c_str(), fragmentSource.c_str(), compileTimeDefines);}

程序加载了顶点着色器和片段着色器,也就是将Shader文件加载到内存中,接下来开始编译Shader脚本了,编译Shader脚本的函数是initWithByteArrays,它的具体实现代码如下所示:

bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeDefines){_program = glCreateProgram();CHECK_GL_ERROR_DEBUG();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;       }    }// 创建和编译片段着色器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;}

程序加载了顶点着色器和片段着色器,也就是将Shader文件加载到内存中,接下来开始编译Shader脚本了,编译Shader脚本的函数是initWithByteArrays,它的具体实现如下所示:

bool GLProgram::compileShader(GLuint* shader, GLenum type, const GLchar* source, const std::string& convertedDefines){GLint status;if(!source)    {return false;    }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"),#endifCOCOS2D_SHADER_UNIFORMS,        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)    {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);}

函数的主要作用是对编写的Shader代码进行逐行解释,整个Shader底层加载编译就完成了,在明白其运行原理后,开始编写逻辑,假设已经有了顶点着色器代码zerklo.vert和片段着色器zerklo.frag,需将其传入到函数的参数中,调用的代码片段如下:

auto glprogram_Zerkalo = GLProgram::createWithFilenames("astronaut/zerkalo.vert", "astronaut/zerkalo.frag");auto _state_Zerkalo = GLProgramState::getOrCreateWithGLProgram(glprogram_Zerkalo);sprite_Zerkalo->setGLProgramState(_state_Zerkalo);

代码片段是加载Shader的顶点着色器和片段着色器,加载完成将其作用到材质上。当然加载Shader 的方式有很多种,也可以直接使用材质脚本加载,后面章节会有具体介绍,下面系列开始介绍顶点索引数据类。



0 0
原创粉丝点击