Cocos2d-x2.1.1-ClippingNodeTest深入分析(二)

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址http://blog.csdn.net/honghaier

红孩儿Cocos2d-X学习园地QQ3群:205100149,47870848


Cocos2d-x2.1.1-ClippingNodeTest深入分析(二)



 

    用冯巩的一句台词:“好久不见,我可想死你们啦!”。红孩儿五月份没有写东西,很对不起大家,红孩儿五月份一直在忙工具箱的事,经过短期发力,工具箱已经可以将动画和界面部分编辑并导出到Cocos2d-x中。另外红孩儿手上的项目进入测试期,就把博客给冷落了。不过大家放心,红孩儿一定会继续坚持把Cocos2d-x的教程进行到底!

 

    好了,书归正传,接上一篇文ClippingNodeTest。我们把后面的部分继续讲完。


第八个实例:RawStencilBufferTest

截图:



说明:

         循环设置模版缓冲各位的值并进行遮罩处理。

原理:

         设置相应的模板缓冲位数据,并通过模板测试比较进行相应区域像素的刷色块与精灵显示。

类代码:


class RawStencilBufferTest : public BaseClippingNodeTest
{
public:
	//析构
    ~RawStencilBufferTest();
	//重载基类函数。
    virtual std::string title();
    virtual std::string subtitle();
    virtual void setup();
    virtual void draw();
	//设置遮罩用的模版缓冲
	virtual void setupStencilForClippingOnPlane(GLint plane);
	//设置显示图像用的模板缓冲
	virtual void setupStencilForDrawingOnPlane(GLint plane);

protected:
	//精灵成员
    CCSprite* m_pSprite;
};


对应CPP:
//模板位索引
static GLint _stencilBits = -1;
// ALPHA参考值
static const GLfloat _alphaThreshold = 0.05f;
//面板数量
static const int _planeCount = 8;
//面板颜色
static const ccColor4F _planeColor[] = {
    {0, 0, 0, 0.65f},
    {0.7f, 0, 0, 0.6f},
    {0, 0.7f, 0, 0.55f},
    {0, 0, 0.7f, 0.5f},
    {0.7f, 0.7f, 0, 0.45f},
    {0, 0.7f, 0.7f, 0.4f},
    {0.7f, 0, 0.7f, 0.35f},
    {0.7f, 0.7f, 0.7f, 0.3f},
};
//析构
RawStencilBufferTest::~RawStencilBufferTest()
{
    CC_SAFE_RELEASE(m_pSprite);
}
//标题
std::string RawStencilBufferTest::title()
{
	return "Raw Stencil Tests";
}
//副标题
std::string RawStencilBufferTest::subtitle()
{
	return "1:Default";
}
//初始化
void RawStencilBufferTest::setup()
{
	//OPENGL相应的接口函数取得程序运行在当前设备的模版缓冲区位数。
glGetIntegerv(GL_STENCIL_BITS, &_stencilBits);
//如果小于3,提示模版缓冲不满足当前的OPENGL视窗要求
    if (_stencilBits < 3) {
        CCLOGWARN("Stencil must be enabled for the current CCGLView.");
}
//创建精灵
m_pSprite = CCSprite::create(s_pPathGrossini);
//占用,对其引用计数器加一。
m_pSprite->retain();
//设置锚点横向居中,纵向靠下。就是设置脚部位置为锚点。
m_pSprite->setAnchorPoint(  ccp(0.5, 0) );
//放大2.5倍
m_pSprite->setScale( 2.5f );
//设置开启ALPHA混合。为什么要开启ALPHA混合?好问题!放在后面回答。
    CCDirector::sharedDirector()->setAlphaBlending(true);
}
//绘制函数
void RawStencilBufferTest::draw()
{    
	//取得屏幕右上角位置
    CCPoint winPoint = ccpFromSize(CCDirector::sharedDirector()->getWinSize());
    //根据面板数量取得每个面板的大小。
    CCPoint planeSize = ccpMult(winPoint, 1.0 / _planeCount);
    //开启模板测试。
glEnable(GL_STENCIL_TEST);
//检测是否有运行BUG
    CHECK_GL_ERROR_DEBUG();
    //遍历设置每个面板
    for (int i = 0; i < _planeCount; i++) {
        //计算出每个遮罩区域的右上角位置,这里计算的结果其实就是右边至上而下的平均分位置。
        CCPoint stencilPoint = ccpMult(planeSize, _planeCount - i);
        stencilPoint.x = winPoint.x;
        //计算出精灵的位置。这里计算的结果为精灵在最下方至左向右的平均分位置。
        CCPoint spritePoint = ccpMult(planeSize, i);
        spritePoint.x += planeSize.x / 2;
        spritePoint.y = 0;
        m_pSprite->setPosition( spritePoint );

		 //设置当前面板的遮罩模板缓冲
        this->setupStencilForClippingOnPlane(i);
	 //检测是否有运行BUG
        CHECK_GL_ERROR_DEBUG();
		 //在屏幕上相应遮罩区域绘制色块,写入模板缓冲相应模板值。
        ccDrawSolidRect(CCPointZero, stencilPoint, ccc4f(1, 1, 1, 1));
        //绘制精灵。
		//先保存当前状态下的矩阵。
        kmGLPushMatrix();
		//设置当前节点的矩阵
        this->transform();
		//调用精灵函数绘制精灵。
        m_pSprite->visit();
		//恢复之前状态下的矩阵。
        kmGLPopMatrix();
        //设置显示图形时的模板缓冲。
        this->setupStencilForDrawingOnPlane(i);
		 //检测是否有运行BUG
        CHECK_GL_ERROR_DEBUG();
        //在屏幕上全屏绘制色块,因为有模版遮罩,所以只有遮罩区域才显示。
        ccDrawSolidRect(CCPointZero, winPoint, _planeColor[i]);
        
//再绘制精灵一遍。
        kmGLPushMatrix();
        this->transform();
        m_pSprite->visit();
        kmGLPopMatrix();
    }
    //关闭模版测试
glDisable(GL_STENCIL_TEST);
//检测是否有运行BUG
    CHECK_GL_ERROR_DEBUG();
}
//设置遮罩用的模版缓冲
void RawStencilBufferTest::setupStencilForClippingOnPlane(GLint plane)
{
	//计算相应平面所在的模板位
GLint planeMask = 0x1 << plane;
//设置为模板缓冲掩码
glStencilMask(planeMask);
//先清空当前画面的模板缓冲
glClearStencil(0x0);
    glClear(GL_STENCIL_BUFFER_BIT);
glFlush();
//设置永远不能通过测试。
glStencilFunc(GL_NEVER, planeMask, planeMask);
//如果测试未通过时将相应像素位置的模版缓冲位的值设为1。
    glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
}
//设置显示图像用的模板缓冲
void RawStencilBufferTest::setupStencilForDrawingOnPlane(GLint plane)
{
	//计算相应平面所在的模板位
GLint planeMask = 0x1 << plane;
//取得对应相应模版位的测试参考值,因为之前的位在循环做遮罩时都设为1,故测试参考值应为相应位为1加后面所有位都为1。
GLint equalOrLessPlanesMask = planeMask | (planeMask - 1);
//设置只有像素对应模板位的模板指等于参考值通过测试。即只有缓冲位值为1的像素位置才会显示当前要绘制的精灵像素。
glStencilFunc(GL_EQUAL, equalOrLessPlanesMask, equalOrLessPlanesMask);
//如果测试未通过时保持模版缓冲位的值不变。
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}

 

    这是一个比较难于理解的实例,也许你看的云里雾里,不过放心,红孩儿的教程一定让你深入的理解这里面的玄机。

    这个实例复杂之外其实就是多了一个精灵,为了更好的理解模版缓冲,咱们先把精灵干掉。然后来运行一下。


  /*
        kmGLPushMatrix();
        this->transform();
        m_pSprite->visit();
        kmGLPopMatrix();
		*/
        
        this->setupStencilForDrawingOnPlane(i);
        CHECK_GL_ERROR_DEBUG();
        
        ccDrawSolidRect(CCPointZero, winPoint, _planeColor[i]);
        /*
        kmGLPushMatrix();
        this->transform();
        m_pSprite->visit();
        kmGLPopMatrix();
		*/



 

    看,这就是通过循环至上而下设置每个遮罩区域后,对屏幕进行刷色块处理的显示效果。

 

    再仔细,再仔细,我们以循环两次时的色块截屏来观察:



这样,我们可能很好的观察到色块是如何刷的。

 

    我们在此基础上打开精灵的显示:



    我们可以看到精灵被渲染了两次,我用黄线标示了精灵的实际遮罩大小,为什么会是这样的区域呢?在这里我们要重点说明一下,当使用ALPHABLEND和ALPHATEST的区别:

    如果你使用ALPHABLEND,实际它的每个像素仍会写入模版缓冲,即使我们认为它是透明的像素。

    而使用ALPHATEST,它被测试为透明镂空的像素在PS阶段完全被干掉了,不会再对背景缓冲起任何作用,包括颜色缓冲,深度缓冲与模版缓冲。

    下面,我们开启ALPHATEST:

//CCDirector::sharedDirector()->setAlphaBlending(true);
//开启ALPHATEST
	CCGLProgram *alphaTestShader = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColorAlphaTest);
    m_pSprite->setShaderProgram(alphaTestShader);

截图显示:



    可以看到,与ALPHABLEND时的不同在于黄框中上方的部分区域是镂空的。好好的记住这一点,我们在做很多效果时会用到。

 

    我们以原来的ALPHABLEND状态再增加一次循环来看一下效果:



 

    可以看到,第三次循染的色块为绿色,精灵我用黄框标示,我们发现在黄框右上角出现一个小区块是没有被涂绿的,这是为什么呢?

 

    在setupStencilForDrawingOnPlane函数中,我们判定可以通过模版测试的条件是:

 

    当前像素的模板缓冲值 = 当前循环次数所在缓冲位的数值 | 之前位都为1。

 

    那右小角的小块呢?它的模板缓冲值是肯定不符合测试条件的。

    它的模板缓冲值为0x100,只是精灵写入了一次。而黄框中其它区域的模板缓冲值为多少呢?0x110.因为与上次循环写入的模板缓冲值0x010做了“或”运算。

如果我们稍稍改动一下:

    把setupStencilForDrawingOnPlane函数中的这句

    GLintequalOrLessPlanesMask = planeMask | (planeMask - 1);

    改为

    GLintequalOrLessPlanesMask = planeMask;

再运行看看:



 

    右上角就被填充了,因为黄框中所有模板缓冲值为0x100,而测试条件只测第三位,所以与0x110运算是可以通过测试的。

 

    瞧,这么一个小程序,里面也是处处玄机。你是否领悟了真正的引擎开发:的乐趣呢?

 

后面的RawStencilBufferTest2~ RawStencilBufferTest6是一堆由此实例派生的各种测试实例,分别代表的意义为:

 

    RawStencilBufferTest2:关闭写入深度缓冲。

    RawStencilBufferTest3:关闭写入深度缓冲,并关闭深度测试。这样会按照渲染的先后顺序来做Z排序,而非深度缓冲。

    RawStencilBufferTest4: RawStencilBufferTest2基础上使用ALPHATEST而非混合。

    RawStencilBufferTest5: RawStencilBufferTest3基础上使用ALPHATEST而非混合。

    RawStencilBufferTest6:这个是取像素的模版缓冲。简单说一下:

//取得幅标题。
std::string RawStencilBufferTest6::subtitle()
{
	return "6:ManualClear,AlphaTest:ENABLE";
}

//初始化
void RawStencilBufferTest6::setup()
{
	//判断所处平台支持
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
	//取得窗口大小
    CCPoint winPoint = ccpFromSize(CCDirector::sharedDirector()->getWinSize());
	//清空模板缓冲值为0
    unsigned char bits = 0;
    glStencilMask(~0);
    glClearStencil(0);
    glClear(GL_STENCIL_BUFFER_BIT);
    glFlush();
	//取得0,0点的像素的模板缓冲值
    glReadPixels(0, 0, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, &bits);
	//创建文字标签来显示模板缓冲值。
    CCLabelTTF *clearToZeroLabel = CCLabelTTF::create(CCString::createWithFormat("00=%02x", bits)->getCString(), "Arial", 20);
    clearToZeroLabel->setPosition( ccp((winPoint.x / 3) * 1, winPoint.y - 10) );
    this->addChild(clearToZeroLabel);
	//清空模板缓冲值为0xAA
    glStencilMask(0x0F);
    glClearStencil(0xAA);
    glClear(GL_STENCIL_BUFFER_BIT);
    glFlush();
	//取得0,0点的像素的模板缓冲值
    glReadPixels(0, 0, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, &bits);
//创建文字标签来显示模板缓冲值。
    CCLabelTTF *clearToMaskLabel = CCLabelTTF::create(CCString::createWithFormat("0a=%02x", bits)->getCString(), "Arial", 20);
    clearToMaskLabel->setPosition( ccp((winPoint.x / 3) * 2, winPoint.y - 10) );
    this->addChild(clearToMaskLabel);
#endif
	//设置遮罩掩码为0xfffffffff
    glStencilMask(~0);
	//调用基类的相应函数。
    RawStencilBufferTest::setup();
}

//设置遮罩用的模版缓冲
void RawStencilBufferTest6::setupStencilForClippingOnPlane(GLint plane)
{
	//取得相应循环对应的位
    GLint planeMask = 0x1 << plane;
	//设置模版缓冲的掩码值。
    glStencilMask(planeMask);
	//设置永远不通过测试
    glStencilFunc(GL_NEVER, 0, planeMask);
	//设置如果不通过测试,就设置相应位的值为1.
    glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
	//全屏绘制白色,写入模板缓冲值。
    ccDrawSolidRect(CCPointZero, ccpFromSize(CCDirector::sharedDirector()->getWinSize()), ccc4f(1, 1, 1, 1));
	//设置永远不通过测试
    glStencilFunc(GL_NEVER, planeMask, planeMask);
	//设置如果不通过测试,就设置相应位的值为1.
    glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
	//关闭深度测试。
    glDisable(GL_DEPTH_TEST);
	//关闭写入深度。
    glDepthMask(GL_FALSE);
	//开始ALPHA测试。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
    glEnable(GL_ALPHA_TEST);
    glAlphaFunc(GL_GREATER, _alphaThreshold);
#else
    CCGLProgram *program = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColorAlphaTest);
    GLint alphaValueLocation = glGetUniformLocation(program->getProgram(), kCCUniformAlphaTestValue);
    program->setUniformLocationWith1f(alphaValueLocation, _alphaThreshold);
    m_pSprite->setShaderProgram(program);
#endif
	//立即刷新缓冲。
    glFlush();
}
//设置显示图像用的模板缓冲
void RawStencilBufferTest6::setupStencilForDrawingOnPlane(GLint plane)
{
	//关闭ALPHATEST
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
    glDisable(GL_ALPHA_TEST);
#endif
	//打开写入深度缓冲。
    glDepthMask(GL_TRUE);
	//打开深度测试。
    glEnable(GL_DEPTH_TEST);
	//调用基类的相应函数。
    RawStencilBufferTest::setupStencilForDrawingOnPlane(plane);
	//立即刷新缓冲。
    glFlush();
}

运行截图:



讲了这么多,那倒底用遮罩能做什么效果呢?为了更好的理解,我用“红孩儿工具箱”来展示遮罩的功能:

 

一.红孩儿工具箱之遮罩动画1-【遮罩显示】

http://v.qq.com/boke/page/t/h/v/t0407iwbmhv.html

 

这个演示是使用模板缓冲来实现图片相应区域的显示。

 

二.红孩儿工具箱之遮罩动画2-【遮罩隐藏】
http://v.qq.com/boke/page/y/c/m/y0407t6szcm.html

这个演示是使用模板缓冲来实现图片相应区域的隐藏。




    我只使用了简单的三张图,就实现了需要大量序列帧才能实现的效果,大大的节省了游戏对内存的需求。所以,这种技法很有用。

 

    衷心的希望大家能有所收获,把游戏做的更漂亮。下节课再见!

 

 


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火云洞红孩儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值