Can someone provide a working shader code for cocos2dx 4?

Hello,
before refering me to the tests or explaining in the theory what a shader is and how code it,
can someone actually share a simple working code to apply to a sprite?
I have been researching for a week now through old threads and tutorials all over the internet and the code has completly change in cocos2dx 4.0, or the code that is supposed to work for that version is not working out for me.
I know what a fragment shader is, a vertex shader, etc… etc… and I have read this aswell

https://docs-cocos-com.translate.goog/cocos2d-x/v4/manual/en/upgradeGuide/spriteTutorial.html?_x_tr_sl=en&_x_tr_tl=es&_x_tr_hl=es&_x_tr_pto=sc

So can someone please simply share a working code showing a shader applied to a sprite so I can literally copy/paste and test it for my self on visual studio and see what is actually going on?
This topic by it self is a bit confusing to me, and when most of the code that I find is deprecated, its is impossible to figure out.

thanks,
R

The shaders that are part of Cocos2d-x v4 should be plentiful to learn from, but if you want to know how to convert between an implementation you may see online to what cocos2d-x requires, then the following may help you.

Check out this shader implementation on Shadertoy: https://www.shadertoy.com/view/XsVSDz

In one of the comments that page, you’ll see a simplified implementation, like this:

void mainImage( out vec4 O, vec2 U )
{
	float t = iTime/.1 + iMouse.x;
	vec2  R =  iResolution.xy, S = vec2(160,100),
          p = ( U+U - R ) / R * S,
          q = vec2(cos(-t / 165.), cos( t / 45.))  * S - p;
    t = 1. + cos( length( vec2(cos( t / 98.),  sin( t / 178.)) * S - p ) / 30.) 
           + cos( length( vec2(sin(-t / 124.), cos( t / 104.)) * S - p ) / 20.) 
           + sin( length(q) / 25. ) * sin(q.x / 20.) * sin(q.y / 15.);
    O = .5 + .5* cos( (iTime+iMouse.y) / vec4(63,78,45,1) + ( t + vec4(0,1,-.5,0) ) *3.14 );
}

Now, the reason I’m posting this up is because it may help you learn how to convert one of those to Cocos2d-x, although in my particular case I’ve converted it to Axmol, which is a fork of Cocos2d-x v4, and it does work:

const std::string shader_plasma_frag =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"varying vec4 v_fragmentColor;\n"
"varying vec2 v_texCoord;\n"
"\n"
"uniform float u_time; // time in seconds\n"
"uniform vec2 u_resolution;\n"
"uniform vec2 u_position;\n"
"\n"
"const float MATH_PI = float(3.14159265359);\n"
"\n"
"void main()\n"
"{\n"
"    float t = u_time / .1 + u_position.x;\n"
"    vec2  R = u_resolution.xy, S = vec2(160, 100),\n"
"        p = (v_texCoord + v_texCoord - R) / R * S,\n"
"        q = vec2(cos(-t / 165.), cos(t / 45.)) * S - p;\n"
"    t = 1. + cos(length(vec2(cos(t / 98.), sin(t / 178.)) * S - p) / 30.)\n"
"        + cos(length(vec2(sin(-t / 124.), cos(t / 104.)) * S - p) / 20.)\n"
"        + sin(length(q) / 25.) * sin(q.x / 20.) * sin(q.y / 15.);\n"
"    gl_FragColor = .5 + .5 * cos((u_time + u_position.y) / vec4(63, 78, 45, 1) + (t + vec4(0, 1, -.5, 0)) * MATH_PI);\n"
"}\n";
// In TestScene.cpp

void TestScene::PlasmaTest()
{
    const auto visibleSize = Director::getInstance()->getVisibleSize();

    auto* program  = backend::Device::getInstance()->newProgram(positionTextureColor_vert, shader_plasma_frag);
    _plasmaProgramState = new ProgramState(program);
    // NOTE THAT PROGRAM AND PROGRAMSTATE ARE NOT AUTORELEASED!
    CC_SAFE_RELEASE_NULL(program); // Must release it here

    auto* rt = RenderTexture::create(1, 1);
    rt->clear(255, 255, 255, 255);

    auto* node = Sprite::createWithTexture(rt->getSprite()->getTexture());
    node->setContentSize(visibleSize);
    node->setStretchEnabled(true);
    node->setPositionNormalized(Vec2(0.5f, 0.5f));
    addChild(node, 1);

    node->setProgramState(_plasmaProgramState);

    auto* touchListener = EventListenerTouchOneByOne::create();
    touchListener->setSwallowTouches(false);

    touchListener->onTouchBegan = [this, node](Touch* t, Event*) -> bool {
        auto loc = t->getLocation();
        loc = node->convertToNodeSpace(loc);
        _plasmaProgramState->setUniform(_plasmaPosLoc, &loc, sizeof(loc));
        return true;
    };
    touchListener->onTouchMoved = [this, node](Touch* t, Event*) {
        auto loc = t->getLocation();
        loc = node->convertToNodeSpace(loc);
        _plasmaProgramState->setUniform(_plasmaPosLoc, &loc, sizeof(loc));
    };
    touchListener->onTouchEnded = [this, node](Touch* t, Event*) {
        auto loc = t->getLocation();
        loc = node->convertToNodeSpace(loc);
        _plasmaProgramState->setUniform(_plasmaPosLoc, &loc, sizeof(loc));
    };
    getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, node);

    _plasmaTimeLoc = _plasmaProgramState->getUniformLocation("u_time");
    _plasmaResolutionLoc = _plasmaProgramState->getUniformLocation("u_resolution");
    _plasmaPosLoc = _plasmaProgramState->getUniformLocation("u_position");

    auto time = 0.f;
    _plasmaProgramState->setUniform(_plasmaTimeLoc, &time, sizeof(time));

    auto resolution = Vec2(node->getContentSize()) / node->getContentSize().width;
    _plasmaProgramState->setUniform(_plasmaResolutionLoc, &resolution, sizeof(resolution));

    auto position = Vec2(node->getContentSize()) / 2;
    _plasmaProgramState->setUniform(_plasmaPosLoc, &position, sizeof(position));
}
// In TestScene.h:
cocos2d::backend::ProgramState* _plasmaProgramState = nullptr;
cocos2d::backend::UniformLocation _plasmaPosLoc;
cocos2d::backend::UniformLocation _plasmaTimeLoc;
cocos2d::backend::UniformLocation _plasmaResolutionLoc;

It should work exactly the same way as the Shadertoy implementation, so if you click anywhere on the screen, it’ll change colors etc…

If you try this and get compilation errors, then you’ll need to sort them out yourself, because as I mentioned earlier, this is working on the Axmol fork of Cocos2d-x v4, so there may be slight differences.

1 Like

hello,
after reading your answer I decided to switch to axmol to be able to follow better your example.
I installed axmol as described in the instructions, but I get the following to errors.

I check the addition dependencies and “lib\Debug\axmol.lib” is there.

Any ideas?

Any questions to axmol shouldn’t be posted here, but rather on the repo discussion page here.

okey, so
I copied that code and modified the line CC_SAFE_RELEASE_NULL(program) to AX_SAFE_RELEASE_NULL(program). He the only two litern complains are the ones shown in the image

Device doesn’t exist apparently and I think in consequence ProgramState complains with “expected a type specifier”.

I tried that instead

_programState = new (std::nothrow) backend::ProgramState(positionTextureColor_vert, shader_plasma_frag);

but it doesn’t work either.

Hello,
I was missing to include the header file for Device. Everything is ok now.

cheers