上一篇我们介绍了cocos2d-x绘制基本图形的基本流程,我们还留下了一个着色器的部分没有讲,本篇内容将从openGL的渲染流程讲起,介绍cocos2d-x中的着色器,openGL的渲染流程如图所示:
openGL的绘制通常就是将顶点数据传输到openGL服务端。我们可以将一个顶点视为一个需要统一处理的数据包,这个包中的数据可以是我们需要的任何数据,通常其中几乎始终会包含位置数据。其他数据可能用来决定一个像素的最终颜色。
顶点着色器:一个复杂的应用程序可能包含许多个顶点着色器,但是在同一时刻只能有一个顶点着色器起作用,它用来进行顶点的变换,可能会非常简单,简单到传递着色器,也可能包含大量的计算。
几何着色器:对几何图元做处理,可以创建新的图元,这个阶段是可选的
细分着色器:顶点着色器存在一些限制,一个就是它们在执行的过程中无法创建额外的几何图形,它们仅仅更新与它们当前所处理的顶点相关数据,无法访问当前图元中其他顶点数据,它包含细分控制着色器和细分计算着色器。
像素着色器(片元着色器):通过编程控制在屏幕上显示颜色的阶段,在这个阶段中,我们使用着色器来计算片元的最终颜色和它的深度值。片元着色器非常强大,在这里我们会使用纹理映射的方式,对顶点处理阶段进行补充。可能还会丢弃不需要处理的片元
简而言之,顶点着色器决定一个图元应该位于屏幕什么位置,而片元着色器使用这些信息来决定摸个片元的颜色是什么。这两个着色器是必选的。另外因为顶点着色器阶段处理的顶点比较少,而在像素着色阶段处理的像素会很多,所以有时会在顶点阶段处理颜色和光照,其余的像素进行插值处理,为了节约效率,当然效果要差些了。
下面来看cocos2d-x的着色器流程,回到上一篇讲的DrawNode的init函数中
//设置着色器 setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_LENGTH_TEXTURE_COLOR));这句话调用setGLProgramState设置着色器状态,setGLProgramState函数代码如下:
void Node::setGLProgramState(cocos2d::GLProgramState* glProgramState) { if (glProgramState != _glProgramState) { CC_SAFE_RELEASE(_glProgramState); _glProgramState = glProgramState; CC_SAFE_RETAIN(_glProgramState); if (_glProgramState) _glProgramState->setNodeBinding(this); } }这段代码判断设置的着色器是否与之前设置的相同,不同就重新设置,在onDraw函数中,会使用设置的着色器
auto glProgram = getGLProgram(); glProgram->use(); glProgram->setUniformsForBuiltins(transform);其中getGLProgram函数就是获得着色器,代码如下:
GLProgram * Node::getGLProgram() const { return _glProgramState ? _glProgramState->getGLProgram() : nullptr; }或者干脆直接在使用时传递使用哪个着色器
auto glProgram = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_LENGTH_TEXTURE_COLOR); glProgram->use(); glProgram->setUniformsForBuiltins(transform);无论哪种方式,我们这里都使用的是cocos2d-x的内置的着色器代码,它们在“cocos目录/cocos/renderer”目录中
具体的详细加载可以看GLProgramCache类的loadDefaultGLProgram函数,这是一个单例类,在初始化的时候就会加载所有的内置着色器,然后这些着色器的代码文本以键值对的方式存储程序中,当使用的时候就通过getGLProgram调用,我们看一下这里使用的 GLProgram :: SHADER_NAME_POSITION_LENGTH_TEXTURE_COLOR:
顶点着色器:ccShader_PositionColorLengthTexture.vert
像素着色器:ccShader_PositionColorLengthTexture.frag
const char* ccPositionColorLengthTexture_vert = STRINGIFY( \n#ifdef GL_ES\n attribute mediump vec4 a_position; attribute mediump vec2 a_texcoord; attribute mediump vec4 a_color; varying mediump vec4 v_color; varying mediump vec2 v_texcoord; \n#else\n attribute vec4 a_position; attribute vec2 a_texcoord; attribute vec4 a_color; varying vec4 v_color; varying vec2 v_texcoord; \n#endif\n void main() { v_color = vec4(a_color.rgb * a_color.a,a_color.a); v_texcoord = a_texcoord; gl_Position = CC_MVPMatrix * a_position; } );
cocos2d-x的内置着色器采用字符串的方式存储,可读性上会差一些,忽略掉“\n”之类的应该还好,着色器使用GLSL,在下一篇中会详细介绍,它类似于C语言,main函数是入口,这里处理了输入进来的颜色,贴图和位置信息,并做相应处理,位置进行了MVP的转换。
下一篇会介绍GLSL
能力不足,水平有限,如有错误,欢迎指出。