• 用BlendFunc实现舞台灯光和刮刮卡效果


    【转】http://code.lovemiao.com/?p=136#more-136

    之前写过一篇《不规则形状按钮的点击判定》,利用了CCRenderTexture创建一块画布,可以在上面随意作画,这次,美术同学又本着把程序员折腾到底的态度,提出了又一奇葩需求,由于原需求设计商业机密,这里仅举个同理的例子说明。

    附带福利图一张:

    6620100920131209195146095

    神马?没看够?还想看看其他人?请看耐心完全文

    要做到上面的效果,glBlendFunc是个很好的选择。glBlendFunc是一个设置图像叠加方式的函数,就是把一张图绘制在画布上的时候,用指定的混色模式,使被绘制的图和原画布上的图进行混色运算,来实现各种混色效果。关于glBlendFunc的使用方法网上有很多教程,具体计算原理这里不再赘述,可参见微软的文档(官网文档排版实在蛋疼):http://msdn.microsoft.com/en-us/library/ms537046,下面介绍下上述效果的实现过程。

    1.分层绘制

    一共有三层图素,原美女图在最底层,第二层是黑色遮罩层,第三层是灯光层。

    image

    如上图,遮罩层是一张略带一点透明的黑色图素,灯光层是只用alpha值标记的黑色灯光形区域(请原谅我是用鼠标画的),要想把美女从黑色遮罩层底下露出来,就要把黑色遮罩层“抠个洞”,然后把“带洞的”遮罩层盖在原图上。

    CCRenderTexture再次派上用场,当做一块临时画布,先把黑色遮罩层画在画布上,然后用灯光层进行混色运算,得到“带洞的”遮罩层。

    2.选择合适的blendFunc

    blendFunc是设置到灯光层上的,如果把灯光层中alpha大于0的像素称为标记像素,则期望的结果是alpha值越大的地方,混色运算后的alpha值越小,黑色遮罩层上相应的地方透明度越大,alpha值为0的像素点,不影响黑色遮罩层。

    这里的黑色遮罩层(实际上已经画在renderTexture上了)就是Destination Color,灯光层就是Source Color,由于灯光层仅起到标记区域的作用,只有alpha值有效,且不应该影响最终结果的RGB值,所以srcFactor选择GL_ZERO,黑色遮罩层作为Destination Color,也是RGB图素的提供者,需要把灯光层标记的位置“抠掉”,所以dstFactor选择GL_ONE_MINUES_SRC_ALPHA。

    现在就可以利用CCRenderTexture和glBlendFunc实现舞台灯光效果了。关键代码如下:

    void BlendFunc::updateTexture()
    {
        renderTexture->beginWithClear(0, 0, 0, 0);
        sprFore->visit();
        sprMask->visit();
        renderTexture->end();
    }
     
    void BlendFunc::initSprites()
    {
        // 初始化所有图素
        sprBg = new CCSprite();
        sprBg->initWithFile("Images/back2.png");
        sprFore = new CCSprite();
        sprFore->initWithFile("Images/fore2.png");
        sprMask = new CCSprite();
        sprMask->initWithFile("Images/mask3.png");
     
        // 初始化CCRenderTexture
        const CCSize &size = sprBg->getContentSize();
        renderTexture = new CCRenderTexture();
        renderTexture->initWithWidthAndHeight(size.width, size.height, kCCTexture2DPixelFormat_RGBA8888);
        renderTexture->setContentSize(size); // CCRenderTexture的contentSize需要手工设置,否则会影响坐标转换结果
     
        CCPoint pos = ccp(250, 150); // 仅作为示例,这里随意设置了一个位置
        renderTexture->setPosition(pos);
        sprBg->setPosition(pos);
        sprFore->ignoreAnchorPointForPosition(true); // CCSprite默认锚点在图素中心点,CCRenderTexture原点在坐下点,在此需要忽略锚点对CCSprite的影响
     
        renderTexture->addChild(sprMask);
        addChild(sprBg);
        addChild(renderTexture);
     
        ccBlendFunc blendFunc;
        blendFunc.src = GL_ZERO;
        blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        sprMask->setBlendFunc(blendFunc);
        updateTexture();
    }

    把sprBg和renderTexture加到父节点中,通过主循环递归地调用visit函数,可以直接被绘制出来,但把sprMask加到renderTexture中,并不会绘制sprMask,除非把renderTexture的autoDraw打开,不过目前最新的Cocosd-X 2.2.1版本还不建议这么做,所以需要靠updateTexture函数来修改renderTexture中的内容,不然renderTexture中始终是一块空白画布。之所以仍然要把sprMask加入到renderTexture中是因为,这样可以让sprMask的onEnter和onExit函数被调用,以保证sprMask生命周期的完整性。

    现在可以通过修改sprMask的位置,照亮任意一位MM了。由于sprMask只有在修改位置的时候才需要重绘,renderTexture大部分时间都在绘制已经画好的texture,这和绘制一张普通的图片是一样的性能开销。

    灯光效果介绍完了,可惜只有一盏灯,如果在updateTexture的时候,保留原画布的内容,在此基础上在不同位置上再画以此sprMask,这样在画布上就有两盏灯的效果了,以此类推,可以有多盏灯的效果。

    void BlendFunc::updateTexture()
    {
        renderTexture->begin();
        sprMovableMask->visit();
        renderTexture->end();
    }
     
    void BlendFunc::initSprites()
    {
        // 代码同上
        ...
        ...
        ...
     
        ccBlendFunc blendFunc;
        blendFunc.src = GL_ZERO;
        blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        sprMovableMask->setBlendFunc(blendFunc);
     
        renderTexture->beginWithClear(0, 0, 0, 0);
        sprFore->visit();
        renderTexture->end();
     
        sprMovableMask->onMoving = [this]{
            updateTexture();
        };
    }

    6620100920131209195310073

    如果把灯光遮罩换成一块橡皮遮罩呢?让橡皮遮罩可以跟随手指一起移动,一张刮刮卡诞生。

  • 相关阅读:
    Spring Boot 的各种start
    Lombok介绍、使用方法和总结
    JS字符串与二进制的相互转化
    java字符串与二进制的相互转化
    DevExpress WPF v18.2新版亮点(六)
    用MyEclipse开发REST Web Service
    .NET界面控件DevExpress发布v18.2.4|附下载
    DevExpress WPF v18.2新版亮点(五)
    「版本升级」MyEclipse CI 2018.12.0正式发布
    MyEclipse使用教程:在Web项目中使用Web片段
  • 原文地址:https://www.cnblogs.com/Siegel/p/5990756.html
Copyright © 2020-2023  润新知