Cocos2dx--使用Shader

Cocos2dx的Shader架构

Cocos2dx的Shader由GLProgram、GLProgramState、GLProgramCache、GLProgramStateCache组成。
GLProgram是Cocos2dx对Program的封装,一般提供一些静态方法,被多个节点复用。而GLProgramState是对GLProgram的封装,提供更加便捷的操作接口,内部记录了Uniform和Attribute,可以看作一个动态的对象。GLProgram和GLProgramState可以对比理解类和对象的区别。
它们分别对应的xxCache类顾名思义用来做缓冲的,如Cocos2dx中所有内置Shader的名字,就在GLProgramCache的loadDefaultGLProgram()方法中,将所有内置的Shader都创建了出来并缓存。

Cocos2dx内置Shader规则

下面我们先来分析下源码,看看Cocos2dx对Shader是怎么封装的,它与一般OpenGL编写的Shader脚本不同。
首先是GLProgram的compileShader()方法中,编译Shader前使用了precision关键字来设定数值的精度。总共有低精度lowp、中精度mediump、高精度highp可以选择,可以为浮点数或整数指定精度,精度越高效果越好,但消耗也越大。另外,顶点着色器支持的精度要比片段着色器高,片段着色器的highp精度的支持对于显卡而言,是可选的。
然后,它还会在Shader脚本前定义一系列Uniform变量。这些都是Cocos2dx内置的Uniform,在Shader中可以直接使用。下面是源码剪切:

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"),#endif
        COCOS2D_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);
}
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";

在初始化GLProgram时,Cocos2dx会自动根据Shader是否使用了相应的Uniform变量来初始化_flags成员变量相对应的属性。在编译Shader时,GLSL的编译器会自动将没有使用到的Uniform移除(用glGetUniformlocation()方法判断,返回-1就是没有使用)。

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_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);

    _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);
}

上面我们分析了Cocos2dx的Shader会内置很多Uniform变量,也知道了这些Uniform变量是否使用了。那么下一个问题,如果使用了,这些Uniform是什么时候被设置的呢?它们的值又是多少呢?
如果是纹理Uniform,在一开始就会被设置为固定的值,上面源码中也可以看到,同时可以使用4个纹理,在渲染的时候,Cocos2dx会将纹理绑定到指定的纹理采样器中,我们在Shader脚本中可以访问这些采样器,而采样器对应的纹理,由Cocos2dx调用OpenGL的glBindTexture()等方法绑定,因此这个Uniform的值不需要我们设置。
而其它的如矩阵、时间、法线、随机数等Uniform,是在运行的时候动态设置的,这些属性是实时变化的,在程序运行时,Cocos2dx会每帧调用setUniformsForBuiltins()自动设置它们。

void GLProgram::setUniformsForBuiltins(const Mat4 &matrixMV)
{
    const auto& matrixP = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

    if (_flags.usesP)
        setUniformlocationWithmatrix4fv(_builtInUniforms[UNIFORM_P_MATRIX],matrixP.m,1);

    if (_flags.usesMV)
        setUniformlocationWithmatrix4fv(_builtInUniforms[UNIFORM_MV_MATRIX],matrixMV.m,1);

    if (_flags.usesMVP)
    {
        Mat4 matrixMVP = matrixP * matrixMV;
        setUniformlocationWithmatrix4fv(_builtInUniforms[UNIFORM_MVP_MATRIX],matrixMVP.m,1);
    }

    if (_flags.usesnormal)
    {
        Mat4 mvInverse = matrixMV;
        mvInverse.m[12] = mvInverse.m[13] = mvInverse.m[14] = 0.0f;
        mvInverse.inverse();
        mvInverse.transpose();
        GLfloat normalMat[9];
        normalMat[0] = mvInverse.m[0];normalMat[1] = mvInverse.m[1];normalMat[2] = mvInverse.m[2];
        normalMat[3] = mvInverse.m[4];normalMat[4] = mvInverse.m[5];normalMat[5] = mvInverse.m[6];
        normalMat[6] = mvInverse.m[8];normalMat[7] = mvInverse.m[9];normalMat[8] = mvInverse.m[10];
        setUniformlocationWithmatrix3fv(_builtInUniforms[UNIFORM_norMAL_MATRIX],normalMat,1);
    }

    if (_flags.usesTime) {
        // This doesn't give the most accurate global time value. // Cocos2D doesn't store a high precision time value,so this will have to do.
        // Getting Mach time per frame per shader using time Could be extremely expensive.
        float time = _director->getTotalFrames() * _director->getAnimationInterval();

        setUniformlocationWith4f(_builtInUniforms[GLProgram::UNIFORM_TIME],time/10.0,time,time*2,time*4);
        setUniformlocationWith4f(_builtInUniforms[GLProgram::UNIFORM_SIN_TIME],time/8.0,time/4.0,time/2.0,sinf(time));
        setUniformlocationWith4f(_builtInUniforms[GLProgram::UNIFORM_COS_TIME],cosf(time));
    }

    if (_flags.usesRandom)
        setUniformlocationWith4f(_builtInUniforms[GLProgram::UNIFORM_RANDOM01],CCRANDOM_0_1(),CCRANDOM_0_1());
}

当然,除了以上内置的Uniform外,Cocos2dx还内置了一些Attribute,但这些变量是需要在Shader脚本中手动输入的,主要是颜色、坐标、纹理坐标、法线等常用属性。它们被定义在CCGLProgram.cpp中,在Renderer进行渲染时,会调用glVertexAttribPointer()设置它们。

// Attribute names
const char* GLProgram::ATTRIBUTE_NAME_COLOR = "a_color";
const char* GLProgram::ATTRIBUTE_NAME_POSITION = "a_position";
const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD = "a_texCoord";
const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD1 = "a_texCoord1";
const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD2 = "a_texCoord2";
const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD3 = "a_texCoord3";
const char* GLProgram::ATTRIBUTE_NAME_norMAL = "a_normal";
const char* GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT = "a_blendWeight";
const char* GLProgram::ATTRIBUTE_NAME_BLEND_INDEX = "a_blendindex";
const char* GLProgram::ATTRIBUTE_NAME_TANGENT = "a_tangent";
const char* GLProgram::ATTRIBUTE_NAME_BInorMAL = "a_binormal";

整个Cocos2dx的Shader不同的规则大体就是这样了,主要是内置了一系列的变量,由引擎自动设置值,我们可以在自己Shader中直接使用。

Cocos2dx中使用Shader示例

我们就编写一个常用的灰化Shader来演示下Cocos2dx中使用Shader的流程。

灰化原理:灰白图片的特征就是每个像素的RGB三个分量的值均相等,值越高颜色越白,反之颜色越黑。常用的算法有3种,平均值、最大值和加权平均值。

首先,我们得分别编写顶点Shader和片段Shader:

//顶点Shader
attribute vec4 a_position;   //位置(内置)
attribute vec2 a_texCoord;   //纹理坐标(内置)
varying vec2 v_texCoord;     //自定义易变变量(写入值)
void main()
{
    gl_Position = CC_PMatrix * a_position;
    v_texCoord = a_texCoord;
}
//片段着色器
//使用加权平均值算RGB分量,即给每个分量指定一个比例,指定比例没有写死,通过一个Uniform变量来设置
varying vec2 v_texCoord;    //接收顶点着色器传递的值(接收值)
uniform vec4 u_grayParam;   //指定rgb颜色分量的比例
void main()
{
    vec4 texColor = texture2D(CC_Texture0,v_texCoord);
    texColor.rgb = texColor.r * u_grayParam.r + texColor.g * u_grayParam.g + texColor.b * u_grayParam.b;
    gl_FragColor = texColor;
}

分别命名为gray.vert和gray.frag保存到磁盘。
好,脚本写完了,下面来看看怎么使用:

#include "renderer/CCGLProgramStateCache.h"
//置灰接口
void grayNode(Node* node)
{
    //对应xxCache的运用,先成缓存中找,不需要每次都重新创建
    GLProgram* program = GLProgramCache::getInstance()->getGLProgram("MyGrayShader");
    if(nullptr == program)
    {
        //本地磁盘加载shader创建program,还一种字符串的形式效率要高createWithByteArrays()
        program = GLProgram::createWithFilenames("gray.vert","gray.frag");
        //将GLProgram对象缓存到Cache中
        GLProgramCache::getInstance()->addGLProgram(program,"MyGrayShader");
    }
    //通过program创建programState,用它来设置上面自定义的Uniform变量的值
    GLProgramState* programState = GLProgramState::getorCreateWithGLProgram(program);
    programState->setUniformVec4("u_grayParam",Vec4(0.2f,0.3f,0.5f,1.0f));
    //调用node节点的setGLProgramState,以后此节点渲染就会使用这个Shader了
    node->setGLProgramState(programState);
}
//使用
auto sprite = Sprite::create(xx.png);
grayNode(sprite);

至此,Cocos2dx中使用Shader流程就讲完了~ 最后说下,怎么把置灰的图片再还原回去呢?OpenGL是个状态机的运行机制,所以只需要把设置的Shader还原回去重新设置就行了。这里Sprite对象认的Shader是GLProgram::SHADER_NAME_POSITION_TEXTRUE_COLOR_NO_MVP,可以通过GLProgramState::getorCreateWithGLProgramName()传入上面字符串常量来获取它的GLProgramState.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是