Shader rendered to texture

Hi, I’d like to have
->a sprite
->apply a shader
->render the shader into a texture
->switch back to display the normal sprite
->use the texture shaded for pixel collision
outdated pixel perfect collision detection complete code
porting to cocos2dx v3.5
–if I set the shader program back to the default shader being used by the game the texture is like the default without the shader alpplied.
–if I don’t switch back the shader is applied to the sprite but the rendered texture is empty
–I tried _rt->end(); Director::getInstance()->getRenderer()->render(); doesn’t work
–seem that I should make an onDraw method, but I can’t figure out how to switch back correctly…
dynamic-texture-gradinet-not-rendering
synchronous-glreadpixel-in-cocos2d-x-v3
custom-mesh-rendering-with-texture-in
how-to-use-fragment-shader-with-cocos2d-x-3-1
this topic try to display the same image in multiple position, seem very similar, as I try to have the same sprite with different shader, one on the screen, one on a texture…
sprite-and-multiple-visits-inside-render-texture
this is the glProgram:

glProgram = new CCGLProgram();
glProgram->retain();
glProgram->initWithVertexShaderFilename("SolidVertexShader.vsh", "SolidColorShader.fsh");
glProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
glProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
glProgram->link();
glProgram->updateUniforms();
glProgram->use();

uniformColorRed = glGetUniformLocation(glProgram->getProgram(), "u_color_red");
uniformColorBlue = glGetUniformLocation(glProgram->getProgram(), "u_color_blue");

and this is the test:

Texture2D *CollisionDetection::spriteTest(cocos2d::CCSprite* spr1, CCRenderTexture* _rt) {
    
    CCRect r1 = spr1->boundingBox();
    
    // Setting the custom shader to be used
    spr1->setShaderProgram(glProgram);
    spr1->getShaderProgram()->use();
    
    
    // Clearing the Secondary Draw buffer of all previous values
    _rt->beginWithClear( 255, 255, 0, 255);
    
    // The below two values are being used in the custom shaders to set the value of RED and BLUE colors to be used
    glUniform1i(uniformColorRed, 255);
    glUniform1i(uniformColorBlue, 0);
    
    // The blend function is important we don't want the pixel value of the RED color being over-written by the BLUE color.
    // We want both the colors at a single pixel and hence get a PINK color (so that we have both the RED and BLUE pixels)
    spr1->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});
    
    // The visit() function draws the sprite in the _rt draw buffer its a Cocos2d-x function
    spr1->visit();
    
    // Setting the shader program back to the default shader being used by our game
//    spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
    // Setting the default blender function being used by the game
//    spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
    
    _rt->end();
//    Director::getInstance()->getRenderer()->render();

    spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));//kCCShader_PositionTextureColor));
        spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

    
    return _rt->getSprite()->getTexture();
}

how can I render a shader to a texture then switch the texture to normal before is rendered on the screen?
thanks

1 Like

override the call to visit function, and call visit function on both original sprite and rendered sprite. you will see both.

thanks for the answer but perhaps I didn’t understood:
this is the app with only add an image to helloworld.cpp


this if I add a rendered texture:

........
    _rt->end();
    spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
    spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

if I delete this rows:

spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
        spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

this adding visit:

void HelloWorld::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags){
    sprite->visit();
    rendered->visit();
    sprite->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
    sprite->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
}

if I delete this rows:

sprite->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
        sprite->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

I obtain the red rectangle…

what I’d like to achieve is a pixel perfect collision detection, the rendered texture on the screen is only for debugging purpose

thanks

in the init function
//init normalSprite
//apply shader
//create renderedSprite [ use scale -1 on y-axis, by default it is inverted]

override visit function
//normalSprite->visit
//renderedSprite->visit

I’ve posted the codes, I think I’ve followed you suggestion correctly but not work, perhaps because visit() make a custom command that is rendered by the new renderer in queue list

Can u post ur complete code

Hi, I’ve made now a very basic code that works a little:
helloworld.h

   CCRenderTexture *_rt;
    Sprite *sprite;
    Sprite *rendered;

in .cpp method init() I added (with this I can see the flippedY texture, is ok):

    sprite=Sprite::create("HelloWorld.png");
    sprite->setPosition(Vec2(0,0));
    this->addChild(sprite);
   _rt = CCRenderTexture::create(500,350);
    _rt->retain();
    _rt->beginWithClear(255, 255, 0, 255);
    sprite->visit();
    _rt->end();
    rendered=CCSprite::createWithTexture(_rt->getSprite()->getTexture());
    rendered->setPosition(ccp(visibleSize.width/2-(1200-1024)/2, visibleSize.height/2+200));
    rendered->setOpacity(150);
    this->addChild(rendered, 1000000);

I changed the button callback:

void HelloWorld::menuCloseCallback(Ref* pSender)
{
//    CCGLProgram *glProgram = new CCGLProgram();
    CCGLProgram *glProgram =GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccPositionTextureColor_noMVP_frag);
    glProgram->retain();
//    glProgram->initWithVertexShaderFilename("SolidVertexShader.vsh", "SolidColorShader.fsh");
    glProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
    glProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
    glProgram->link();
    glProgram->updateUniforms();
    glProgram->use();

    sprite->setAnchorPoint(Vec2(0,0));
    _rt->setAutoDraw(false);
    _rt->setKeepMatrix(true);
//    sprite->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});
    sprite->setShaderProgram(glProgram);
//    sprite->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
    sprite->getShaderProgram()->use();

    _rt->beginWithClear(255, 255, 0, 255);
    sprite->setPosition(Vec2(240, 160));
    sprite->visit();

    _rt->end();
    sprite->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
//    sprite->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
    
    Director::getInstance()->getRenderer()->render();
//    sprite->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
    sprite->setPosition(Vec2(0, 0));

    rendered->setTexture(_rt->getSprite()->getTexture());
}

with: CCGLProgram *glProgram =GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, ccPositionTextureColor_noMVP_frag); I thought that the image is unchanged, instead is scaled and translated
if I uncomment: sprite->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST}); the image is disapper

thanks

I’m still stuck here, after the shader is applyed the texture is translated and scaled, no matter what I have in the shader or if I use what provided in the standard ccPositionTextureColor_noMVP_vert ecc…
do someone has an idea?
thanks