[cocos2d-x] 《我所理解的Cocos2d-x》上机实验2-动态设置着色器参数

第四章 8.2.2 动态设置着色器参数

这个例子看得时候很流畅,本以为会很容易调通。结果发现参数总是传不进去,卡了很久。最后请教了一位很有经验的同事,才找到问题所在。还是看实际代码介绍吧。

顶点着色器: dynamic_shader.vert

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_PMatrix * a_position;
    v_fragmentColor = a_color;
    
    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);
    float high = (dis-u_center.z)/u_center.z;

    v_high = high;
}

最开始的时候把uniform写成attribute了,编译的时候也不报错,但参数根本不会读取的。这是存储限定符,在166页写得很清楚。

片段着色器:dynamic_shader.frag

varying lowp vec4 v_fragmentColor;
varying lowp float v_high;

uniform vec4 u_highColor;
uniform vec4 u_lowColor;

void main(void)
{
    if (v_high > 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;
}

这两个文件可以当作素材放到res文件夹下,也可以像类一样引用,还有一种方法是直接以字符串的方式读入。

接下来是在HelloWorld这个场景中使用。这里就是出问题的地方了。

bool HelloWorld::init()
{
    ... ... // 略掉的原有内容
 
// 书中使用的是LayerColor 但如果不改变LayerColor的onDraw方法的话 是无法实现效果的
//    LayerColor* pLayer = LayerColor::create(Color4B::WHITE);
//    addChild(pLayer);
    
// 这里写了一个简单的LayerColor功能的类 在onDraw的时候添加了一行代码
    DynamicShaderLayer* pLayer = DynamicShaderLayer::create();
    addChild(pLayer);
    
    Size size = Director::getInstance()->getWinSize();
    _center = Vec2(size.width / 2, size.height / 2);
    _halfDis = sqrtf(size.width * size.width + size.height * size.height) / 2;

// 没有小球 就用现有素材代替了
    auto sprite = Sprite::create("HelloWorld.png");
    sprite->setPosition(_center);
    addChild(sprite, 10);
    
    auto program = GLProgram::createWithFilenames("res/dynamic_shader.vert", "res/dynamic_shader.frag");
    auto pstate = GLProgramState::create(program);
    pLayer->setGLProgramState(pstate);

    Color4B highColor = Color4B::RED;
    Color4B lowColor = Color4B::GREEN;

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

// 这个回调设置也和书中不一样,书中的参数表应该是弄错了
    pstate->setUniformCallback("u_center", [this](GLProgram* p, Uniform* u)
    {
        p->setUniformLocationWith3f(u->location, _center.x, _center.y, _halfDis);
    });
    
    Device::setAccelerometerEnabled(true);
    auto listener = EventListenerAcceleration::create([this, size, sprite](Acceleration* acc, Event* event)
                                                      {
        _center.x += acc->x * 9.81f;
        _center.y += acc->y * 9.81f;

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

        sprite->setPosition(_center);
    });

    getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
 
    return true;

}

如注释中所说,按照LayerColor的渲染方式是没有办法把参数传进去的。上面用到的DynamicShaderLayer代码如下:

DynamicShaderLayer.h

#include "cocos2d.h"
using namespace cocos2d;

class DynamicShaderLayer : public Node
{
public:
    CREATE_FUNC(DynamicShaderLayer);
    
private:
    virtual bool init() override;
    
    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
    void onDraw(const Mat4 &transform, uint32_t flags);

    Color4F  _squareColors[4];
    CustomCommand _customCommand;
    Vec3 _noMVPVertices[4];
};

DynamicShaderLayer.cpp

#include "DynamicShaderLayer.hpp"

bool DynamicShaderLayer::init()
{
    if (!Node::init())
    {
        return false;
    }
    
// 这里是LayerColor的默认着色器程序,在这个例子当中我们是要替换着色器程序的,所以注释掉也没有影响。
//  setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP));
    
    return true;
}

void DynamicShaderLayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    Size s = Director::getInstance()->getWinSize();
    
    _noMVPVertices[0] = Vec3(0.0, 0.0, 0.0);
    _noMVPVertices[1] = Vec3(s.width, 0.0, 0.0);
    _noMVPVertices[2] = Vec3(0.0, s.height, 0.0);
    _noMVPVertices[3] = Vec3(s.width, s.height, 0.0);
    
    _squareColors[0] = Color4F::WHITE;
    _squareColors[1] = Color4F::WHITE;
    _squareColors[2] = Color4F::WHITE;
    _squareColors[3] = Color4F::WHITE;
    
    _customCommand.init(getGlobalZOrder(), transform, flags);
    _customCommand.func = CC_CALLBACK_0(DynamicShaderLayer::onDraw, this, transform, flags);
    renderer->addCommand(&_customCommand);
}

void DynamicShaderLayer::onDraw(const Mat4 &transform, uint32_t flags)
{
    getGLProgram()->use();
    getGLProgram()->setUniformsForBuiltins(transform);
    
// 这里就是LayerColor没有的一行 这一句操作是会将参数传入GLProgram的
    getGLProgramState()->applyUniforms();
    
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
    
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

对照一下LayerColor的onDraw方法:

void LayerColor::onDraw(const Mat4& transform, uint32_t /*flags*/)
{
    getGLProgram()->use();
    getGLProgram()->setUniformsForBuiltins(transform);
    
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
    
    //
    // Attributes
    //
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);

    GL::blendFunc( _blendFunc.src, _blendFunc.dst );

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
}

不知道书中的cocos版本是多少的,我看了3.0的和3.15.1的都是没有这一行的。不过如果我们这里不用LayerColor,用一个带纹理的Sprite,再换成同样的着色器程序,是可以看到参数能传过去的。那就说明3.0的QuadCommand 和 3.15.1的TrianglesCommand是可以带参数的。那位同事也是从这里找到线索,发现需要apply一下的。

另外,一个调试技能。XCode运行当中是可以通过“相机”这个按钮看GPU状态的,这就可以达到调试着色器的效果。

最终效果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值