zoukankan      html  css  js  c++  java
  • 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,性能一样无损,还能够支持渐变,但也有其局限性。且听下回分解。

  • 相关阅读:
    Git-远程版本库
    Git-Git分支
    Git-Git里程碑
    Git-冲突解决
    Git-Git库管理
    Git-Git克隆
    Git-改变历史
    Git-历史穿梭
    RHCE考试
    RHCSA考试
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6939914.html
Copyright © 2011-2022 走看看