coco2dx精灵和背景遮挡_[cocos2dx]精灵遮罩

首先得理解一些东西。

1.理解颜色混合。精灵有个成员函数:setBlendFunc(),这个函数以一个ccBlendFunc类型的变量为参数。这个ccBlendFunc是个结构体。这个结构体中有两个变量:src 和 dest. 举个例子:

代码:

ccBlendFunc  spriteBlend;

spriteBlend.src = GL_ONE;

spriteBlend.dst = GL_ZERO;

pSprite->setBlendFunc(spriteBlend);

假设精灵pSprite是源颜色.则setBlendFunc的作用就是把精灵pSprite的各个像素的R,G,B,A分量和源颜色因子1.0(src = GL_ONE)相乘.  如果精灵pSprite是目标颜色,则setBlendFunc的作用就是把精灵pSprite的各个像素的R,G,B,A分量和目标颜色因子(dst = GL_ZERO)相乘.

如何界定pSprite是源颜色还是目标颜色呢?

如果这个时候还存在一个精灵pSpriteOther.如果pSprite先调用visit(), 然后pSpriteOther后调用visit()(visit()的作用是递归的渲染精灵和他的孩子节点)。。。则先调用visit()的为目标颜色,后调用visit的为源颜色。即:pSprite是目标颜色 ,pSpriteOther为源颜色。

2.做精灵的遮罩效果为什么要用CCRenderTexture这个类。

你可能会觉得我们只需要先把mask(遮罩)精灵渲染上去,然后再渲染被遮罩的精灵,并且指定这两个精灵的blendFunc就行了。可是,实际上这样是行不通的!

因为被渲染上去的mask精灵下面如果还有其他的精灵。这样的话被渲染到mask精灵之上的精灵在做颜色混合的时候会出现意想不到的结果。达不到我们做遮罩的效果。

这样的话,我们需要一个比较干净的画板,这个干净的画板只有两个精灵在做颜色混合。这样的话这两个精灵在做颜色混合的时候就能达到我们想要的结果。不会受到不干净的背景造成的混合误差。这个背景就是CCRenderTexture.

当然如果我们的layer上只有精灵做混合的话就用不着CCRenderTexture了。但是实际项目中基本上是不能的。

OK。看看我们的Code.

CCSize size = CCDirector::sharedDirector()->getWinSize();//创建干净的画板

CCRenderTexture *pRt =CCRenderTexture::create(size.width,size.height);

CCAssert(pRt,"RenderTexture is invalid");

addChild(pRt);

pRt->setPosition(size.width/2,size.height/2);//创建遮罩图片

CCSprite *pMask = CCSprite::create("CalendarMask.png");

CCAssert(pMask,"mask sprite is invalid");

pMask->setPosition(CCPointMake(pMask->getContentSize().width/2, pMask->getContentSize().height/2));//创建被遮罩图片

CCSprite *pFlower = CCSprite::create("Calendar1.png");

CCAssert(pFlower,"Flower sprite is invalid");

pFlower->setPosition(CCPointMake(pFlower->getContentSize().width/2, pFlower->getContentSize().height/2));//先设置好 遮罩精灵 和 被遮罩精灵 在被渲染的时候采用什么样的颜色混合法则

ccBlendFunc maskBlend ={GL_ONE, GL_ZERO};

ccBlendFunc flowerBlend={GL_DST_ALPHA, GL_ZERO};

pMask->setBlendFunc(maskBlend);

pFlower->setBlendFunc(flowerBlend);//开始把各种精灵渲染到画板上

pRt->begin();//先渲染遮罩精灵。但是因为有个画板先被渲染。所以pMask是第二个被渲染的,即后被渲染。//所以在这一刻pMask是源颜色。调用pMask->visit()的时候吧精灵pMask上的每个像素的RGBA分量和1.0相乘。//所以遮罩图片被元模原样的渲染出来.

pMask->visit();//再渲染被遮罩的精灵.在这一刻,之前先有pMask被渲染。所以pFlower后被渲染。pFlower就是源颜色。之前的pMask就是目标颜色。//调用pFlower->visit()的时候,精灵pFlower上的对应像素的RGBA分量和pMask上的对应像素的A分量相乘.因为前面设置了GL_DST_ALPHA。

pFlower->visit();//停止渲染到画板

pRt->end();

上面看注释就懂了。

先看遮罩图片(PNG)目标颜色

这个遮罩图片是个不规则的边缘的图片,其本事是个矩形。除了白色区域有像素外,其他区域没像素,是全透明的。以上图片中显浅蓝色的区域是我截取的时候故意这样做的 。实际上这一区域是全透明的。

再看被遮挡图片(源颜色)

采用GL_DST_ALPHA把遮挡图片对应像素的RGBA分量和 被遮挡图片的A分量相乘.这样的话,遮挡图片中透明的区域在被遮挡图片上对应的区域就全透明了。

效果如下图。

黑色的区域是layer的背景.

话中貌似用CCRenderTexture的方式效率很低下,但是本人也没深究过。

2、高效率遮罩

先说下模板缓冲(stencil buffer),这在05年还算是一个比较普及的技术。cocos2d-x现在的版本是不支持stencil buffer的,但opengl es是支持的。

可以简单的动手改造一下:

创建stencil buffer。在ES1Renderer.m文件中找到resizeFromLayer方法,将if (depthFormat_){}大括号中的代码替换成以下内容:

if(depthFormat_)

{if( !depthBuffer_ )

glGenRenderbuffersOES(1, &depthBuffer_);

glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_);if( multiSampling_ )

glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_, depthFormat_,backingWidth_, backingHeight_);elseglRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, backingWidth_, backingHeight_);

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);//add by frankyang at 2012/5/8

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);//bind color buffer

glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);

}

设置stencil buffer格式。在AppController.mm中找到的didFinishLaunchingWithOptions方法,将其中的depthFormat参数改为GL_DEPTH24_STENCIL8_OES,如下:

//Add the view controller's view to the window and display.

window =[[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

EAGLView*__glView =[EAGLView viewWithFrame: [window bounds]

pixelFormat: kEAGLColorFormatRGBA8//depthFormat: GL_DEPTH_COMPONENT16_OES

depthFormat:GL_DEPTH24_STENCIL8_OES

preserveBackbuffer: NO

sharegroup:nil

multiSampling:NO

numberOfSamples:0];

设置每帧渲染开始时清除stencil buffer。在CCDirector.cpp中找到drawScene方法,将其中

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

改成

glClear(GL_COLOR_BUFFER_BIT | GL_COLOR_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)

这样就可以正确清除stencil buffer。

启动模板测试,设置模板函数。这里要用到三个函数:

glEnable(GL_STENCIL_TEST);

glStencilFunc(GL_ALWAYS,0x1, 0x1);

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

第一个是启用模板测试,第二个是设置模板测试函数,第三个是设置模板缓冲操作方式。

模板测试简单来说就是先往模板缓冲中写入模板值,然后渲染时根据模板测试结果来决定像素是否写入color buffer。

具体解释大家可以看这个帖子深入了解OpenGL-模板测试

为了灵活的写入模板值,我借鉴了Quaz2D中maskLayer的概念,在要渲染的Layer前后插入MaskBeginLayer和MaskEndLayer。

用MaskBeginLayer来填充模板缓冲,并设定好之后需要的模板测试函数;用MaskEndLayer来恢复模板测试状态。

voidMaskBeginLayer::visit()

{if (getChildrenCount() != 0) {

glEnable(GL_ALPHA_TEST);

glAlphaFunc(GL_GREATER,0.0);

glEnable(GL_STENCIL_TEST);

glStencilFunc(GL_ALWAYS,0x1, 0x1);

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

CCLayer::visit();

glDisable(GL_ALPHA_TEST);

glStencilFunc(GL_NOTEQUAL,0x1, 0x1);

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

}

}voidMaskEndLayer::visit()

{

glDisable(GL_STENCIL_TEST);

CCLayer::visit();

}

这里要注意透明像素也会写入stencil buffer,所有特别用了alphatest。

经过真机测试,这样实现mask性能是无损的。由于不影响alpha blend,使用起来比较灵活。唯一不好的是mask不支持渐变,要么全透,要么全部透。

现在我在研究直接用alpha blend操作实现mask,性能一样无损,还可以支持渐变,但也有其局限性,且听下回分解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值