• cocos源码分析--用Sprite加载自定义着色器


    本文写一个使用动态更新属性变量的自定义着色器。在这个例子中,小图标的位置根据手指的触摸而移动,以屏幕重点为参照物,屏幕中向下的部分根据手指的点击乘以一个绿色的颜色值,向上的部分乘以一个红色的颜色值。

    可以把计算过程分为三个步骤

    1 利用手指的点击计算出小图标当前的位置,并将位置传入到顶点着色器中,因为他对每个顶点都是一样的,所以我们可以使用一个全局变量u_center

    2 顶点着色器根据顶点与小球的距离,计算出一个-1~1的权重值,这个值作为易变变量v_high 传递给片段着色器。大于0表示手指向上点击,小于0表示向下倾斜

    3 经过光栅化插值的权重值在片段着色器中用来修改片段的颜色,大于0表示向上倾斜,乘以一个红色的颜色值,反之,乘以一个绿色的颜色值。

    我们把自定义着色器作用在一个Sprite元素中,因为Sprite执行过程中会对顶点属性,全局变量的值或者回调方法进行访问。

    首先我们新建名为dynamic_shader.vert和dynamic_shader.frag

    const char* dynamic_shader_vert = STRINGIFY(
    
    attribute vec4 a_position;
    attribute vec4 a_color;
    
    uniform vec3 u_center;//小图标坐标
    varying vec4 v_fragmentColor;
    varying float v_high;//权重值
    
    void main()
    {
        gl_Position = CC_MVPMatrix * a_position;
        v_fragmentColor=a_color;
        //求四个顶点与center的距离dis
        vec2 disV=vec2(a_position.x-u_center.x,a_position.y-u_center.y);
        float dis=sqrt(disV.x*disV.x+disV.y*disV.y);
        //u_center.z为斜对角距离的一半,通过这样计算出-1~1的权重值
        float high=(dis-u_center.z)/u_center.z;
        v_high=high;
    }
    const char* dynamic_shader_frag = STRINGIFY(
    
    
    #ifdef GL_ES
    
    precision lowp float;
    
    #endif
    
    
    varying vec4 v_fragmentColor;
    varying float v_high;
     
    uniform vec4 u_highColor;//向上偏移颜色 红色
    uniform vec4 u_lowColor;
                                                
                                                
    void main()
    {
        
        if(v_high>0.0)//权重值大于0,比如小图标在右上角,左下角的顶点的权重值是大于0的
            gl_FragColor=v_fragmentColor*u_highColor*v_high;
        else
            gl_FragColor=v_fragmentColor*u_lowColor*-v_high;
        
        vec4 white=vec4(1.,1.,1.,1.);
        //gl_FragColor=gl_FragColor+white*(1. -gl_FragColor.a)*white;
        //如果不加白色,中心的权重值为0,他的位置的gl_FragColor就会是黑色,加上一个白色的底,更好看
        gl_FragColor=gl_FragColor+white*(1.0 -gl_FragColor.a);//最边缘的 .a为1 ,中心的.a为0,所以中心为白色
        
       // gl_FragColor=vec4(gl_FragColor.r,gl_FragColor.g,gl_FragColor.b,0.4);
        
        
    }
    );

    我们这里要把这个作为sprite的着色器,但是里面没有纹理,其实这个sprite的作用就和LayerColor差不多了,但是sprite在执行着色过程中会执行我们后期赋值的顶点属性和全局属性,或者回调函数,而LayerColor不会执行这些,体现不出如何使用自定义着色器,所以这里把sprite作为演示对象。

    以下是代码

     //小图标
        auto size=Size(480, 320);
        auto dot=Sprite::create("aaa.png");
        scene->addChild(dot,10);
        dot->setScale(0.2);
        dot->setPosition(size.width/2,size.height/2);
        //自定义着色器的sprite
        auto sprite=Sprite::create("aaa.png");
        scene->addChild(sprite,5);
        sprite->setContentSize(designSize);
        sprite->setColor(Color3B::WHITE);
        sprite->setPosition(size.width/2,size.height/2);
        
        
        auto _halfDis=sqrtf(size.width*size.width+size.height*size.height)/2;
        auto _center=Vec2(size.width/2,size.height/2);
        
     
        // 创建GLProgram,传递我们自定义的着色器
        auto program=GLProgram::createWithByteArrays(dynamic_shader_vert, dynamic_shader_frag);
        auto pstate=GLProgramState::create(program);
        // 把sprite的着色器设置为创建的pstate
        sprite->setGLProgramState(pstate);
        auto _highColor=Color4B::RED;
        auto _lowColor=Color4B::GREEN;
        
        /*
         opengl对顶点属性值的归一化是按照数据类型计算的,他并不能识别颜色值的意义,因此利用GL
         的归一化会导致错误的颜色值,所以这里将颜色值在客户端归一化
         */
    //赋值全局变量,这两个颜色在过程中是不变的,所以我们可以这样设置
    pstate->setUniformVec4("u_highColor",Vec4(_highColor.r/255.0,_highColor.g/255.0,_highColor.b/255.0,_highColor.a/255.0)); pstate->setUniformVec4("u_lowColor",Vec4(_lowColor.r/255.0,_lowColor.g/255.0,_lowColor.b/255.0,_lowColor.a/255.0)); //u_center代码手指的移动,过程中是变化的,所以我们用setCallback的形式设置。
    //这里的回调是在 _glProgramState->apply(_mv);中执行 pstate
    ->setUniformCallback("u_center",[&_center,&_halfDis](GLProgram* glProgram, Uniform* uniform){ glProgram->setUniformLocationWith3f(uniform->location, _center.x, _center.y, _halfDis); // CCLog(">>>>>>>>%f",_halfDis); // CCLog(">>>>>>>>========%f %f",_center.x,_center.y); }); auto touchListener = EventListenerTouchOneByOne::create(); touchListener->onTouchBegan =[this,size,dot,&_center,scene](Touch*touch, Event*event)->bool{ // auto location = scene->convertToNodeSpace(touch->getLocation()); auto location = touch->getLocation(); _center.x=location.x; _center.y=location.y; if(_center.x<0)_center.x=0; else if (_center.x>size.width)_center.x=size.width; if(_center.y<0)_center.y=0; else if (_center.y>size.height)_center.y=size.height; dot->setPosition(_center); return false; }; Director::sharedDirector()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, scene);

    这里关键的一点就是对U_center属性的设置。在callback中对其进行重新赋值,原理是在以下代码中

    void GLProgramState::apply(const Mat4& modelView)
    {
        //使用当前程序
        applyGLProgram(modelView);
        //执行顶点的相关操作
        applyAttributes();
        //执行全局变量的相关操作
        applyUniforms();
    }
    void GLProgramState::applyUniforms()
    {
        // set uniforms
        for(auto& uniform : _uniforms) {
            uniform.second.apply();
        }
    }
    void UniformValue::apply()
    {
        if(_useCallback) {
            (*_value.callback)(_glprogram, _uniform);
        }
        else
        {
            switch (_uniform->type) {
                case GL_SAMPLER_2D:
                    _glprogram->setUniformLocationWith1i(_uniform->location, _value.tex.textureUnit);
                    GL::bindTexture2DN(_value.tex.textureUnit, _value.tex.textureId);
                    break;
    
                case GL_INT:
                    _glprogram->setUniformLocationWith1i(_uniform->location, _value.intValue);
                    break;
    
                case GL_FLOAT:
                    _glprogram->setUniformLocationWith1f(_uniform->location, _value.floatValue);
                    break;
    
                case GL_FLOAT_VEC2:
                    _glprogram->setUniformLocationWith2f(_uniform->location, _value.v2Value[0], _value.v2Value[1]);
                    break;
    
                case GL_FLOAT_VEC3:
                    _glprogram->setUniformLocationWith3f(_uniform->location, _value.v3Value[0], _value.v3Value[1], _value.v3Value[2]);
                    break;
    
                case GL_FLOAT_VEC4:
                    _glprogram->setUniformLocationWith4f(_uniform->location, _value.v4Value[0], _value.v4Value[1], _value.v4Value[2], _value.v4Value[3]);
                    break;
    
                case GL_FLOAT_MAT4:
                    _glprogram->setUniformLocationWithMatrix4fv(_uniform->location, (GLfloat*)&_value.matrixValue, 1);
                    break;
    
                default:
                    CCASSERT(false, "Invalid UniformValue");
                    break;
            }
        }
    }

    _value是一个联合体,可以是float数组,可以是纹理结构体,也可以是lamada函数,如下

     union U{  //联合,U可能是下面任何一个值
            float floatValue;
            int intValue;
            float v2Value[2];
            float v3Value[3];
            float v4Value[4];
            float matrixValue[16];
            struct {
                GLuint textureId;
                GLuint textureUnit;
            } tex;
            std::function<void(GLProgram*, Uniform*)> *callback;
    
            U() { memset( this, 0, sizeof(*this) ); }
            ~U(){}
            U& operator=( const U& other ) {
                memcpy(this, &other, sizeof(*this));
                return *this;
            }
        } _value;

    如果运行没有问题的话,效果如图所示:

    以上就是自定义着色器的使用过程,以及怎样对已有元素使用自定义的着色器程序。这种着色器子系统是开发者不需要继承就可以使用自定义着色器。

  • 相关阅读:
    Java基础(01)--简介及基础语法
    高数(01)--函数、极限、连续
    未来行业及趋势
    HttpRunner基础使用一:
    web自动化中js操作的操作应用
    django框架学习六:优化views.py文件,使用rest_framework中的APIVew和Response返回
    django框架学习五:djangorestframework中ModelSerializer的关联字段的生成
    django框架学习四:引入djangorestframework中ModelSerializer
    django框架学习三:djangorestframework中序列化器的优化:添加单字段、多字段、自定义函数的校验
    django框架学习三:使用DRF框架,引入序列化器,实现对数据库的增删改查操作
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/9233571.html
Copyright © 2020-2023  润新知