Ok,
I am trying out some custom shaders based of the ShaderTestSprite suite and following this tutorial: http://www.raywenderlich.com/4428/how-to-mask-a-sprite-with-cocos2d-2-0
I dont get the result shown in that tutorial page though. The tutorial shows a sprite image masked by a secondary image. My result is that only one or the other of the images are being rendered depending on the order that the texture is bound in the shader draw call. Can anyone see what I am doing wrong in the code below (MaskSprite is based off the ShaderSprite from the test suite, the code follows at the bottom)?
My result is that the mask image (CalendarMask.png) is rendered with the portion that should be transparent as opaque, and the portion that should be opaque is transparent. The actual sprite image that was to be masked is not rendered at all (Calendar1.png)
My custom MaskSprite (ported from the linked tutorial above)
class MaskSprite : public ShaderSprite, public ShaderSpriteCreator
{
public:
MaskSprite();
void draw();
private:
GLuint _maskLocation;
protected:
virtual void buildCustomUniforms();
virtual void setCustomUniforms();
Texture2D* _maskTexture;
};
MaskSprite::MaskSprite() {
_fragSourceFile = "Mask.frag";
_maskTexture = TextureCache::getInstance()->addImage("CalendarMask.png");
}
void MaskSprite::buildCustomUniforms() {
_maskLocation = glGetUniformLocation( getShaderProgram()->getProgram(), "u_mask");
}
void MaskSprite::setCustomUniforms() {
glActiveTexture(GL_TEXTURE1);
GL::bindTexture2D(_maskTexture->getName());
getShaderProgram()->setUniformLocationWith1i(_maskLocation, 1);
}
void MaskSprite::draw() {
GL::enableVertexAttribs(cocos2d::GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX );
BlendFunc blend = getBlendFunc();
GL::blendFunc(blend.src, blend.dst);
getShaderProgram()->use();
getShaderProgram()->setUniformsForBuiltins();
setCustomUniforms();
//
// Attributes
//
#define kQuadSize sizeof(_quad.bl)
long offset = (long)&_quad;
// vertex
int diff = offsetof( V3F_C4B_T2F, vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
// texCoods
diff = offsetof( V3F_C4B_T2F, texCoords);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
// color
diff = offsetof( V3F_C4B_T2F, colors);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glActiveTexture(GL_TEXTURE0);
CC_INCREMENT_GL_DRAWS(1);
}
The mask shader:
#ifdef GL_ES
precision lowp float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D CC_Texture0;
uniform sampler2D u_mask;
void main()
{
vec4 texColor = texture2D(CC_Texture0, v_texCoord);
vec4 maskColor = texture2D(u_mask, v_texCoord);
vec4 finalColor = vec4(texColor.r, texColor.g, texColor.b, maskColor.a * texColor.a);
gl_FragColor = v_fragmentColor * finalColor;
}
The ShaderSprite from the test suite:
template
class ShaderSpriteCreator
{
public:
static spriteType* createSprite(const char* pszFileName)
{
spriteType* pRet = new spriteType();
if (pRet && pRet->initWithFile(pszFileName))
{
pRet->autorelease();
}
else
{
CC_SAFE_DELETE(pRet);
}
return pRet;
}
};
class ShaderSprite : public Sprite
{
public:
ShaderSprite();
~ShaderSprite();
bool initWithTexture(Texture2D* texture, const Rect& rect);
void draw();
void initProgram();
void listenBackToForeground(Object *obj);
protected:
virtual void buildCustomUniforms() = 0;
virtual void setCustomUniforms() = 0;
protected:
std::string _fragSourceFile;
};
ShaderSprite::ShaderSprite()
{
}
ShaderSprite::~ShaderSprite()
{
NotificationCenter::getInstance()->removeObserver(this, EVNET_COME_TO_FOREGROUND);
}
void ShaderSprite::listenBackToForeground(Object *obj)
{
setShaderProgram(NULL);
initProgram();
}
bool ShaderSprite::initWithTexture(Texture2D* texture, const Rect& rect)
{
if( Sprite::initWithTexture(texture, rect) )
{
NotificationCenter::getInstance()->addObserver(this,
callfuncO_selector(ShaderSprite::listenBackToForeground),
EVNET_COME_TO_FOREGROUND,
NULL);
this->initProgram();
return true;
}
return false;
}
void ShaderSprite::initProgram()
{
GLchar * fragSource = (GLchar*) String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile.c_str()).c_str())->getCString();
auto pProgram = new GLProgram();
pProgram->initWithVertexShaderByteArray(ccPositionTextureColor_vert, fragSource);
setShaderProgram(pProgram);
pProgram->release();
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->addAttribute(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
getShaderProgram()->addAttribute(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
getShaderProgram()->addAttribute(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->link();
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->updateUniforms();
CHECK_GL_ERROR_DEBUG();
buildCustomUniforms();
CHECK_GL_ERROR_DEBUG();
}
void ShaderSprite::draw()
{
GL::enableVertexAttribs(cocos2d::GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX );
BlendFunc blend = getBlendFunc();
GL::blendFunc(blend.src, blend.dst);
getShaderProgram()->use();
getShaderProgram()->setUniformsForBuiltins();
setCustomUniforms();
GL::bindTexture2D( getTexture()->getName());
//
// Attributes
//
#define kQuadSize sizeof(_quad.bl)
long offset = (long)&_quad;
// vertex
int diff = offsetof( V3F_C4B_T2F, vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
// texCoods
diff = offsetof( V3F_C4B_T2F, texCoords);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
// color
diff = offsetof( V3F_C4B_T2F, colors);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
CC_INCREMENT_GL_DRAWS(1);
}