cocos2d-x 精灵遮罩

转自:http://bbs.9ria.com/thread-220210-1-4.html

首先得理解一些东西。

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_);
   else
       glRenderbufferStorageOES(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来恢复模板測试状态。

void MaskBeginLayer::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);
  }
}
void MaskEndLayer::visit()
{
  glDisable(GL_STENCIL_TEST);

  CCLayer::visit();
}

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

经过真机測试,这样实现mask性能是无损的。因为不影响alpha blend,使用起来比較灵活。唯一不好的是mask不支持渐变。要么全透,要么所有透。

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

时间: 2024-11-07 07:13:41

cocos2d-x 精灵遮罩的相关文章

cocos2dx box2d使用(一)

出处:https://www.evernote.com/shard/s262/sh/bf561bda-0458-4502-835b-903846f81d68/68e6634bddec74c5a2d5f8dd839eba0a 本例子使用的是cocos2d-x 3.0版本 首先创建一个cocos2d的c++工程,这里就不多做说明了!本例子的项目名称是MyGame 其次,在MyGame的工程设置里Build Settings下,把CC_ENABLE_CHIPMUNK_INTEGRATION=1改为CC

IOS游戏开发相关网站

首先记录并分享下我收集到的几个自学时最常用到的一些网站链接. 直接进入正题: 1-Stack Overflow http://stackoverflow.com/ 这个是国外类似于百度知道的网站,但是专注于回答程序开发相关问题,我有非常多的问题都是通过在这里查询解决的,这是我们百度知道所不知道的事情. 2-iOS Developer Library http://developer.apple.com/library/ios/navigation/#section=Resource%20Type

【Cocos2D-x 3.5实战】坦克大战(2)游戏开始界面

关于游戏的素材都是在网上到处搜集到的,然后自己再用二流的ps技术修修改改的,所以有可能混在一起有点不搭调(没有办法啊,没有美工Orz.. 项目已经建立好了,然后我们需要把我们下载的素材放到Resources文件夹.(游戏完成以后,我会把素材包上传的..主要现在没有找完) 设置屏幕大小: 在vs中打开创建的项目,打开AppDelagate.cpp文件 在AppDelegate::applicationDidFinishLaunching()中添加代码 if(!glview) { glview =

如何在Cocos2D 1.0 中掩饰一个精灵(六)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 掩饰一个精灵:实现代码 打开HelloWorldLayer.m并且在init方法上方添加如下方法: - (CCSprite *)maskedSpriteWithSprite:(CCSprite *)textureSprite maskSprite:(CCSprite *)maskSprite { // 1 CCRenderTexture * rt = [CCRe

使用ClippingNode对精灵进行遮罩处理

在制作一个消除游戏时,有这样一个情况:方块从顶部往下面掉落,在进入布局前,是不能显示的,不然影响视觉体验.那么,既然此方块已经被加入到渲染树了,那么怎么能让其在一部分中不显示,而在另一部分中显示呢? 这就要用到遮罩处理了.在cocos2d-x中,提供了一个裁剪节点ClippingNode,可以实现一个裁剪效果,那么被裁剪掉的部分,就是等于被遮罩了.可能其效果更多,我未深刻研究,只是拿它用来处理上面的问题.官方对此类的介绍在: http://www.cocos2d-x.org/docs/manua

cocos2d 2.x在opengl es 2.0 下自定义着色器来创建特别酷的特效(译)

cocos2d 2.x在opengl es 2.0 下自定义着色器来创建特别酷的特效(译) (2012-12-24 13:22:17) 转载▼ 标签: it cocos2d opengl 着色器 渲染 翻译:弹涂鱼 PS:欢迎加入开发群:285275050 本文翻译自:http://www.raywenderlich.com/10862/how-to-create-cool-effects-with-custom-shaders-in-opengl-es-2-0-and-cocos2d-2-x#

cocos2d 游戏开发实战

文章转自:http://uliweb.clkg.org/tutorial/read/40 6   cocos2d 游戏开发实战 6.1   创建cocos2d项目 6.2   cocos2d v3 "hello world" 6.2.1   显示一个飞船 6.3   精灵 6.4   開始 space viking 之旅 6.4.1   添加 sneakyinput 6.5   精灵的移动效果,旋转效果 6.6   定时器效果 6.7   启动 cocos2d,默认竖屏 6.8   检

Cocos2d-x 让精灵随手指移动起来二(简单实现)

void HelloWorld::ccTouchMoved(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) { CCSize winSize = CCDirector::sharedDirector()->getVisibleSize(); if (m_ship) { CCPoint pos = touch->getDelta();//获取触点当前位置与较早前的位置的差距 CCPoint currentPos = m_ship->get

Cocos 3D功能初探学习笔记(2)---3D精灵

个人原创,欢迎转载,转载请注明地址,专栏地址http://blog.csdn.net/bill_man 在2D阶段,最常用的绘制单元毫无疑问是精灵,通过精灵,我们可以将图片绘制到屏幕上:3d阶段,我们需要绘制在屏幕上的变成了美术人员通过3dmax等工具做出来的3d模型,绘制3D精灵是cocos最早的一批功能之一,也是比较重要的功能之一,本节我们就来学习3D精灵的绘制. 3D精灵支持的格式包括obj,c3t,c3b,不同点如下 obj:是3dmax导出的文件格式,但是不支持模型动画的导出. c3t