Implementing Shader on whole scene not on a single sprite [Solved]

1I have a shader downloaded from fourm thread of cocos2d-x , which converts the sprite to greyscale ( black and white)

void TouchScene::graySprite(Node * sprite)
{
if(sprite)
{
GLProgram * p = new GLProgram();
p->initWithFilenames(“gray.vsh”, “gray.fsh”);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
p->link();
p->updateUniforms();
sprite->setGLProgram§;
}
}

It works but only for a single sprite or when called in loop for each sprite, i want my whole world or scene to be in grayscale , what should i do ?

Current Usage

graySprite(sprite)

I think you can capture the scene via RenderTexture, apply a shader to it and draw it instead.
But I’m not sure how it will effect the performance.

@Obg1 Thanks for replying RenderTexture will first render our whole scene in a buffer then pass it to our GL programme to give us visual output but that definitely looks like a costly procedure , can you please tag a guy who can give more light on this procedure

What does your scene consist of? If Sprites are created for every rendered aspect of the scene then you are probably best off using this graySprite method, or deriving a new class from Sprite and adding the shader in init() and then using this sprite class instead of Sprite::create you would use GraySprite::create.

I think that if you want to gray-scale UI and other aspects of the scene that are rendered using different shaders by default then the Render Texture is really the only good option. The simplest approach would be to setup a frame buffer object (FBO) and have the main camera render to this FBO. Then create a second identical camera that renders only a sprite using the FBO as the texture. You could render everything into a RenderTexture directly, but this would likely require changes to your more new/modified code than the camera method.

Note that it will be more costly to render a post-processed image because you have to render all the sprites once before hand and the cost for rendering a default colored sprite is basically the same as rendering each sprite using a gray-scale shader. There is a cost, but higher-end mobile devices (or desktops) it should be negligible. YMMV.

Using FBO
Big window, small framebuffer?.

Discussion of FBO Concept being added to the engine
http://discuss.cocos2d-x.org/t/camera-refactoring-and-framebufferobject-feature/21593.

Edit: The relevant cpp-test class for camera fbo is CameraFramebufferTest.

3 Likes

@stevetranby Thanks for replying I have 10 - 15 sprites to convert to grayscale , so i thought manually shade them each as 10-15 is a small number but it gives a slight lag or jerk as soon as my shade loop is invoked , I have described this problem on a new thread here is the link Lag or jerk as soon i implement shader [Code Inside][Solved]{Add Glprog to cache}

Thank You :smiley:

This is my first implementation of what you suggested, inspired by the test. Seems like it works. Just adding a second camera with a new fbo worked too but it caused everything to draw twice, seems like doing things this way doesn’t do that. I don’t understand much of what I wrote but it works, thanks @stevetranby

cocos2d::GLProgram* program = cocos2d::GLProgram::createWithFilenames(
    "shaders/bloom.vert",
    "shaders/bloom.frag"
);
if (program)
{
    program->link();

    auto glview = cocos2d::Director::getInstance()->getOpenGLView();
    cocos2d::experimental::FrameBuffer* fbo = cocos2d::experimental::FrameBuffer::getOrCreateDefaultFBO(glview);
    auto render_target = fbo->getRenderTarget();

    const cocos2d::Size size = cocos2d::Director::getInstance()->getWinSizeInPixels();


    cocos2d::experimental::FrameBuffer* frameBuffer = cocos2d::experimental::FrameBuffer::create(1, (unsigned)size.width, (unsigned)size.height);
    cocos2d::experimental::RenderTarget* renderTarget = cocos2d::experimental::RenderTarget::create((unsigned)size.width, (unsigned)size.height);

    cocos2d::Camera* default_camera = cocos2d::Camera::getDefaultCamera();

    frameBuffer->attachRenderTarget(renderTarget);
    default_camera->setFrameBufferObject(frameBuffer);

    cocos2d::Camera* second_camera = cocos2d::Camera::create();
    //pretty sure this means that the only thing this camera will see if stuff masked with USER1
    second_camera->setCameraFlag(cocos2d::CameraFlag::USER1);
    //second_camera->setDepth(-1);
    GET_RUNNING_SCENE()->addChild(second_camera);

    cocos2d::Texture2D* texture = frameBuffer->getRenderTarget()->getTexture();
    cocos2d::Sprite* sprite = cocos2d::Sprite::createWithTexture(texture);
    sprite->setGLProgram(program);
    sprite->setFlippedY(true);
    sprite->setPosition(get_center_pos());
    sprite->setGlobalZOrder(15);
    sprite->setCameraMask((unsigned short)cocos2d::CameraFlag::USER1);
    GET_RUNNING_SCENE()->addChild(sprite);
    CCLOG("shader success");
}
else
{
    CCLOG("\n\nglimmer shader failed!!!!!");
}
2 Likes

Haha, sorry for the multiple deleted posts, I just realized that you said that it is in fact working for you with your code. I was going to offer how to try and fix duplicate drawing issues. Let me know if you are experiencing issues still.

Haha thanks! I don’t think I’ve noticed a double drawing since using that code, but I’m glad you were ready to help! Thanks again.