I'm making a rain scene with 4 different rain drop textures, each rain drop sprite randomly choose a rain drop texture when it's created and added to the screen. All the rain drop sprite share a same shader.
The problem is, when I add a new rain drop onto the screen, all former rain drops that have been added to the scene change its texture to be the same as the new rain drop.
my code is as follows:
local function addOneRainDrop()
local rainStyleNumber = math.random(1,4)
local rainDrop = cc.Sprite:create("rainDrop"..tostring(rainStyleNumber)..".png")
rainShader:use()
rainShader:updateUniforms()
rainShader:setUniformsForBuiltins()
gl.activeTexture(GL_TEXTURE1) -- here may be the reason
gl.bindTexture(GL_TEXTURE_2D, rainDrop:getTexture():getName())
gl.activeTexture(GL_TEXTURE2)
gl.bindTexture(GL_TEXTURE_2D, rainNormal[rainStyleNumber]:getName())
gl.activeTexture(GL_TEXTURE0)
rainDrop:setGLProgram(rainShader)
rainDropLayer:addChild(rainDrop)
end
In my shader, I just sample CC_Texture0, CC_Texture1, CC_Texture2 and output color. It's not the shader's problem, I think. I don't know how Cocos2d manage it's shader's state and uniforms for different sprites, maybe directly inherit from CCSprite, overwrite "draw" and manage the texture by my self can solve the problem but it's a little bit complicated.
Any better ideas?
=============================================================================
UPDATE:
I find out that I can use GLProgramState to store uniforms for each sprite. Quote from cocos2d's website
A GLProgram can be used by thousands of Nodes, but if different uniform values are going to be used, then each node will need its own GLProgramState
So I changed my code to following:
local function addOneRainDrop()
local rainStyleNumber = math.random(1,4)
local rainDrop = cc.Sprite:create("rainDrop"..tostring(rainStyleNumber)..".png")
local glprogramstate = cc.GLProgramState:getOrCreateWithGLProgram(rainShader);
glprogramstate:setUniformTexture("rainDrop", rainDrop:getTexture():getName());
glprogramstate:setUniformTexture("textureBackground", bg:getTexture():getName());
glprogramstate:setUniformTexture("rainDropNormals", rainNormal[rainStyleNumber]:getName());
rainDrop:setGLProgramState(glprogramstate);
rainDropLayer:addChild(rainDrop)
end
Unfortunately, all the sprites still use the same texture. Does this have something to do with batchedNode?
Your issue is with you utilisation of getOrCreateWithGLProgram() which always returns the same GLProgramState because it caches using your rainShader as key.
What I've done is:
static GLProgram* getOrCreateShader(std::string name, const GLchar* vert, const GLchar* frag)
{
auto cache = GLProgramCache::getInstance();
auto prog = cache->getGLProgram(name);
if(prog == nullptr)
{
prog = GLProgram::createWithByteArrays(vert, frag);
cache->addGLProgram(prog, name);
}
return prog;
}
My method creates a program from a GLchar buffer, but you can create a new one which uses disk files.
auto program = getOrCreateShader("rainShaderName", vertexProgram, fragmentProgram);
auto programState = GLProgramState::create(program);
Sprite* sprite = Sprite::create("yourSpriteImage.png");
programState->setUniformTxeture("u_texture", sprite->getTexture());
sprite->setGLProgramState(programState);
This way, the shader program will only be created once, and you'll have a unique GLProgramState per sprite.