Cocos2d-x 3.2:通过ClippingNode实现一个功能完善的跑马灯公告(2)-程序员宅基地

技术标签: ui  游戏  

Cocos2d-x 3.2:通过ClippingNode实现一个功能完善的跑马灯公告(2)

本文转载至深入理解Cocos2d-x 3.x:一步一步通过ClippingNode实现一个功能完善的跑马灯公告(2)

 

本文的主要内容是根据跑马灯解析ClippingNode实现原理。本文涉及到Cocos2d-x 3.x的渲染机制以及部分opengl的知识。

首先看看上一篇文章中说到的跑马灯的简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//设置模板    
auto stencil = Sprite::create();    
//设置显示区域大小    
stencil->setTextureRect(Rect(0, 0, 50, 30));    
     
//设置跑马灯文字    
auto text = Label::createWithSystemFont( "-1dasdasdasd efadaewfevgds dfhrthrbgrg1-" "" , 24);    
//设置锚点    
text->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);    
     
//创建裁剪节点    
auto clippingNode = ClippingNode::create(stencil);    
//设置节点位置    
clippingNode->setPosition(Vec2(700, 400));    
//显示模板内的内容    
clippingNode->setInverted( false );    
//添加显示内容    
clippingNode->addChild(text, 2);    
//加入到UI树    
addChild(clippingNode);


1.跑马灯代码分析

从创建裁剪节点这里开始分析

1
2
//创建裁剪节点    
auto clippingNode = ClippingNode::create(stencil);

这一句话实现了怎么样的功能呢?走进去看看(create函数中,实际上调用的是init函数,所以这里就直接展示init函数了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool  ClippingNode::init(Node *stencil)  
{  
     CC_SAFE_RELEASE(_stencil);  
     _stencil = stencil;  
     CC_SAFE_RETAIN(_stencil);  
       
     _alphaThreshold = 1;  
     _inverted =  false ;  
     // get (only once) the number of bits of the stencil buffer  
     static  bool  once =  true ;  
     if  (once)  
     {  
         glGetIntegerv(GL_STENCIL_BITS, &g_sStencilBits);  
         if  (g_sStencilBits <= 0)  
         {  
             CCLOG( "Stencil buffer is not enabled." );  
         }  
         once =  false ;  
     }  
       
     return  true ;  
}

首先设置_stencil ,这里实际上与setStencil方法一样的(在上文有说过这个方法的作用)

1
2
3
4
5
6
void  ClippingNode::setStencil(Node *stencil)  
{  
     CC_SAFE_RETAIN(stencil);  
     CC_SAFE_RELEASE(_stencil);  
     _stencil = stencil;  
}

然后设置_alphaThreshold ,实际上与setAlphaThreshold是一样的(在上文有说过这个方法的作用)

1
2
3
4
void  ClippingNode::setAlphaThreshold(GLfloat alphaThreshold)  
{  
     _alphaThreshold = alphaThreshold;  
}

其次设置_inverted ,实际上与setInverted方法是一样的(在上文有说过这个方法的作用)

1
2
3
4
void  ClippingNode::setInverted( bool  inverted)  
{  
     _inverted = inverted;  
}

然后调用一个opengl的API,

1
glGetIntegerv(GL_STENCIL_BITS, &g_sStencilBits);

从 逻辑上可以看到这个API在类第一次被创建的时候才会调用。那么这个方法的作用是什么呢,他的作用是将模板缓存中的每一个像素的位数赋值给 g_sStencilBits,如果这个值小于0,说明不支持模板缓存。模板缓存的作用就是将绘图的范围限定在屏幕的固定区域,这个区域可以是一个复杂的 图形,这个区域称之为绘图模板。

此时,已经将跑马灯的实现代码分析得差不多了,接下来看看具体的渲染部分。


2.渲染代码分析

根据Cocos2d-x的渲染机制,渲染部分从visit函数开始分析。以下是visit的实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
void  ClippingNode::visit(Renderer *renderer,  const  Mat4 &parentTransform, uint32_t parentFlags)  
{  
     if (!_visible)  
         return ;  
       
     //父节点坐标转换  
     uint32_t flags = processParentFlags(parentTransform, parentFlags);  
   
     //设置gl使用的矩阵类型  
     Director* director = Director::getInstance();  
     CCASSERT(nullptr != director,  "Director is null when seting matrix stack" );  
     director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
     director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);  
   
     //使用组渲染指令  
     _groupCommand.init(_globalZOrder);  
     renderer->addCommand(&_groupCommand);  
     //下面的渲染指令都会加入到组渲染指令中,一起渲染  
     renderer->pushGroup(_groupCommand.getRenderQueueID());  
   
     //渲染前 _beforeVisitCmd是一个自定义渲染指令(CustomCommand),  
     //作用就是在渲染的这个指令的时候,实际上执行的是ClippingNode::onBeforeVisit这个函数  
     _beforeVisitCmd.init(_globalZOrder);  
     _beforeVisitCmd.func = CC_CALLBACK_0(ClippingNode::onBeforeVisit,  this );  
     renderer->addCommand(&_beforeVisitCmd);  
     //alpha测试(上文说到:Alpha测试的作用通过一句话解释就是:所有像素的透明度值低于某个阀值的统统抛弃,不绘制到屏幕上。)  
     if  (_alphaThreshold < 1)  
     {  
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WINDOWS || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)  
#else  
         //引擎提供GLProgram类来处理着色器相关操作  
         //下述代码的具体作用就是将_alphaThreshold与alphaValueLocation关联,然后将_stencil与着色器程序program关联,  
         //着色器程序program的作用就是执行ALPHA_TEST  
         GLProgram *program = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_ALPHA_TEST_NO_MV);  
         GLint alphaValueLocation = glGetUniformLocation(program->getProgram(), GLProgram::UNIFORM_NAME_ALPHA_TEST_VALUE);  
         program->use();  
         program->setUniformLocationWith1f(alphaValueLocation, _alphaThreshold);  
         setProgram(_stencil, program);  
#endif  
   
     }  
     //渲染模板  
     _stencil->visit(renderer, _modelViewTransform, flags);  
       
     //渲染模板后  
     //作用就是在渲染的这个指令的时候,实际上执行的是ClippingNode::onAfterDrawStencil  
     _afterDrawStencilCmd.init(_globalZOrder);  
     _afterDrawStencilCmd.func = CC_CALLBACK_0(ClippingNode::onAfterDrawStencil,  this );  
     renderer->addCommand(&_afterDrawStencilCmd);  
   
     //下面是渲染所有的子节点  
     int  i = 0;  
     if (!_children.empty())  
     {  
         sortAllChildren();  
         // draw children zOrder < 0  
         for ( ; i < _children.size(); i++ )  
         {  
             auto node = _children.at(i);  
               
             if  ( node && node->getLocalZOrder() < 0 )  
                 node->visit(renderer, _modelViewTransform, flags);  
             else  
                 break ;  
         }  
         // self draw  
         this ->draw(renderer, _modelViewTransform, flags);  
           
         for (auto it=_children.cbegin()+i; it != _children.cend(); ++it)  
             (*it)->visit(renderer, _modelViewTransform, flags);  
     }  
     else  
     {  
         this ->draw(renderer, _modelViewTransform, flags);  
     }  
   
     //渲染后  
     //作用就是在渲染的这个指令的时候,实际上执行的是ClippingNode::onAfterVisit  
     _afterVisitCmd.init(_globalZOrder);  
     _afterVisitCmd.func = CC_CALLBACK_0(ClippingNode::onAfterVisit,  this );  
     renderer->addCommand(&_afterVisitCmd);  
   
     //将组渲染命令推出栈,作用是让后续的渲染命令不加入到组渲染命令中  
     renderer->popGroup();  
   
     director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
}

代码中已经写了比较多的解释了,在这里就只看代码中出现了三个比较特殊的函数,分别是

1
2
3
ClippingNode::onBeforeVisit          //渲染前 首先执行  
ClippingNode::onAfterDrawStencil     //渲染模板后 其次  
ClippingNode::onAfterVisit           //渲染后 最后执行

首先开看看onBeforeVisit的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void  ClippingNode::onBeforeVisit()  
{  
     // 静态变量,用于记录当前程序一共用到的模版缓冲遮罩数量。 每次渲染都会加1  
     s_layer++;  
   
     // 当前模版缓冲位的参数值  
     GLint mask_layer = 0x1 << s_layer;  
     // 模版缓冲位的参数值的掩码  
     GLint mask_layer_l = mask_layer - 1;  
     // 上面两个值做或运算的结果值  
     _mask_layer_le = mask_layer | mask_layer_l;  
   
     // 获取是否使用模版缓冲  
     _currentStencilEnabled = glIsEnabled(GL_STENCIL_TEST);  
     // 取得一些参数,并且赋值给相关变量  
     glGetIntegerv(GL_STENCIL_WRITEMASK, (GLint *)&_currentStencilWriteMask);         //当前写入的模板掩码参数  
     glGetIntegerv(GL_STENCIL_FUNC, (GLint *)&_currentStencilFunc);                   //当前模板函数  
     glGetIntegerv(GL_STENCIL_REF, &_currentStencilRef);                              //当前模板参考值  
     glGetIntegerv(GL_STENCIL_VALUE_MASK, (GLint *)&_currentStencilValueMask);        //当前模板掩码  
     glGetIntegerv(GL_STENCIL_FAIL, (GLint *)&_currentStencilFail);                   //当前模板测试失败后的操作  
     glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, (GLint *)&_currentStencilPassDepthFail);   //当前模板测试通过,深度测试失败后的操作  
     glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, (GLint *)&_currentStencilPassDepthPass);   //当前模板测试通过,深度测试通过后的操作  
   
     // 开启模板测试  
     glEnable(GL_STENCIL_TEST);  
     // 检测开启模板测试的时候是否存在OpenGL错误  
     CHECK_GL_ERROR_DEBUG();  
   
     // 设置模版缓冲的掩码值    
     glStencilMask(mask_layer);  
   
     //取得是否可以写入模版掩码参数  
     glGetBooleanv(GL_DEPTH_WRITEMASK, &_currentDepthWriteMask);  
   
     // 禁止写入深度缓冲  
     glDepthMask(GL_FALSE);  
   
     // 永远不能通过测试  
     glStencilFunc(GL_NEVER, mask_layer, mask_layer);  
     // 根据是否反向运算来决定如果测试不能通过时是否将相应像素位置的模版缓冲位的值设为0。   
     glStencilOp(!_inverted ? GL_ZERO : GL_REPLACE, GL_KEEP, GL_KEEP);  
   
     // 用白色绘制一下屏幕矩形,因为都不能通过嘛,所以就全屏的模版缓冲位的值都被设为0  
     drawFullScreenQuadClearStencil();  
   
     // 永远不能通过测试  
     glStencilFunc(GL_NEVER, mask_layer, mask_layer);  
     // 根据是否反向运算来决定如果测试不能通过时是否将相应像素位置的模版缓冲位的值设为当前参数值  
     glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO, GL_KEEP, GL_KEEP);  
   
     // 如果需要alpha测试,并且是下述平台,则在这里执行alpha测试,否则在visit函数中执行  
     if  (_alphaThreshold < 1) {  
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WINDOWS || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)  
         // 获取是否允许alpha测试  
         _currentAlphaTestEnabled = glIsEnabled(GL_ALPHA_TEST);  
         // 获取一些参数  
         glGetIntegerv(GL_ALPHA_TEST_FUNC, (GLint *)&_currentAlphaTestFunc);  //当前alpha测试函数  
         glGetFloatv(GL_ALPHA_TEST_REF, &_currentAlphaTestRef);               //当前alpha参考值  
         // 开启alpha测试  
         glEnable(GL_ALPHA_TEST);  
         // 检测开启alpha测试的时候是否存在OpenGL错误  
         CHECK_GL_ERROR_DEBUG();  
         // 执行并且传入参数  
         glAlphaFunc(GL_GREATER, _alphaThreshold);  
#else  
           
#endif  
     }  
   
     //Draw _stencil  
}

然后是onAfterDrawStencil

的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void  ClippingNode::onAfterDrawStencil()  
{  
     // 还原alpha测试状态  
     if  (_alphaThreshold < 1)  
     {  
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WINDOWS || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)  
         glAlphaFunc(_currentAlphaTestFunc, _currentAlphaTestRef);  
         if  (!_currentAlphaTestEnabled)  
         {  
             glDisable(GL_ALPHA_TEST);  
         }  
#else  
// 在其他平台暂时没有找到解决方法去还原状态 233  
#endif  
     }  
   
     // 还原深度测试写入状态  
     glDepthMask(_currentDepthWriteMask);  
   
     // 这里设置如果当前模版缓冲中的模版值与运算结果相等则保留相应像素  
     glStencilFunc(GL_EQUAL, _mask_layer_le, _mask_layer_le);  
     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);  
   
     // draw (according to the stencil test func) this node and its childs  
}

最后是onAfterVisit的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void  ClippingNode::onAfterVisit()  
{  
     // 还原模板状态  
     glStencilFunc(_currentStencilFunc, _currentStencilRef, _currentStencilValueMask);  
     glStencilOp(_currentStencilFail, _currentStencilPassDepthFail, _currentStencilPassDepthPass);  
     glStencilMask(_currentStencilWriteMask);  
     if  (!_currentStencilEnabled)  
     {  
         glDisable(GL_STENCIL_TEST);  
     }  
   
     // 结束使用当前模版缓冲位数,就减1.以保证下次还能正常使用  
     s_layer--;  
}

本文涉及到许多关于opengl的相关知识,例如深度测试,模板测试,alpha测试等,以及着色器的使用、渲染机制等。

本文部分注释来自:Cocos2d-x 2.1.1-ClippingNodeTest 深入分析

转载于:https://www.cnblogs.com/dudu580231/p/4560315.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34127717/article/details/93250992

智能推荐

本机MySQL数据库安装_mysql本地安装-程序员宅基地

文章浏览阅读823次。本机MySQL数据库安装比较简单,但坑比较多,很容易出现安装好了但是在某些时候不能使用的情况;另外在卸载的时候,也容易出现卸载不干净,造成二次安装失败的情况,所以在安装前,一定要检查清楚,本地是否已经安装了MySQL。_mysql本地安装

QGIS二次开发(C++/Qt):栅格计算器_qgis二次开发 c++-程序员宅基地

文章浏览阅读652次,点赞15次,收藏6次。编写一个具有加、减、乘、除、对数、指数计算功能的栅格图层计算工具,计算结果以tiff格式的栅格文件输出。思路:1.自行设计界面,逐个实现计算功能;2.调用QGIS原生栅格计算器。本文使用第二种方法,直接调用QGIS的栅格计算器。_qgis二次开发 c++

记一次 java.io.IOException: Attempted read from closed stream-程序员宅基地

文章浏览阅读4.4k次,点赞6次,收藏2次。在一次项目问题排查过程中,发现总是 报错 java.io.IOException: Attempted read from closed stream,根据异常的堆栈分析,定位到了问题的根源。问题示例如下:public class Main { public static void main(String[] args) throws ClientProtocolException,..._java.io.ioexception: attempted read from closed stream.

Python中数据清洗缺失值,重复值和异常值的处理_df_replace_outliers = df.replace( df['a'],np.nan, -程序员宅基地

文章浏览阅读2.1k次。1 缺失值的处理1.1 常规缺失值的处理np.nan,NaN,None,NaT(时间数据类型的缺失值)等df.isnull()1.判断每一个df中的数据是否为缺失值,是则返回True,否则返回False2.可以利用True为1,False为0的特性,sum()即为缺失值数量df.notnull()判断每个df中的值是否不为缺失值,是不为缺失值返回True,不是不为缺失值则返回Falsedf.dropna() 1.删除缺失值,可以指定axis来控制删除出现缺失值的行或者列 2.使用dropna_df_replace_outliers = df.replace( df['a'],np.nan, )

SQLite3 极简教程 & Go 使用 SQLite 内存模式操作数据结构_golang sqlite 内存数据库 附加-程序员宅基地

文章浏览阅读1.1w次。SQLite 简介关键词: RDBMS (embedded), CSQLite is a database engine written in the C language. It is not a standalone app; rather, it is a library that software developers embed in their apps...._golang sqlite 内存数据库 附加

Struts1介绍_struts1引用包-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏7次。struts:[strʌts]支柱; 撑杆; 支杆; 支撑; 趾高气扬的步态; 高视阔步的样子;引入strutsModel1结构如图1所示: Model1是一个以JSP文件为中心的模式,在这种模式中JSP页面不仅负责表现逻辑,也负责控制逻辑。专业书籍上称之为逻辑耦合在页面中,这种处理方式,对一些规模很小的项目如:一个简单的留言簿,也没什么太大的坏处,实际上,人们开始接触一些对自己来说是新的东西的时候,比如,用JSP访问数据库时,往往喜欢别人能提供一个包含这一切的单个JSP页面,因为这样在.._struts1引用包

随便推点

【全解析 | PTA】浙大版《Python 程序设计》题目集-第三章_len('3//11//2018'.split('/'))的结果是_____。-程序员宅基地

文章浏览阅读5.2k次,点赞52次,收藏111次。Python/PTA/浙大版/Python程序设计/题解_len('3//11//2018'.split('/'))的结果是_____。

成功粉碎北信源监控程序vrvedp_m.exe ,vrvrf_c64.exe,svchost.exe,vrvrf_c.exe-程序员宅基地

文章浏览阅读1.7w次,点赞4次,收藏12次。公司安装了北信源的监控软件,用360粉碎了大部分北信源的程序文件,但是有几个程序进程始终开机启动,进程无法杀死,一度成了我的一块心病!后来不知咋的灵光一闪,彻底将其粉碎的毛都不剩,现将成功经验分享给大家;电脑基本环境:win10 专业版,360安全卫士粉碎操作步骤(因我电脑已经粉碎了北信源,所以图片以其他文件做示范):找到北信源残余卸载不到的程序文件所在的位置(就是这些东西vr..._vrvedp_m.exe

码云托管小项目-本地笔记文件管理系统_学习文档笔记托管码云-程序员宅基地

文章浏览阅读3.2k次。项目描述这是一个很简单的类博客系统,旨在方便管理本地笔记文档,整体采用spring+springmvc+angularjs+bootstrap+markdown实现,主要包括分类、查看、搜索等功能。项目背景由于记忆有限,学的东西难免忘记,因此就需要一定的笔记来帮助记忆。 之前基本都是在CSDN用博客,或者有道云笔记,或者直接本地文件系统的文档来充当笔记。 这些方式都有他们的优势..._学习文档笔记托管码云

.Net 连接 Mysql 8.0.29-30版本异常: Character set utf8mb3 is not supported by .Net Framework_character set 'utf8mb3' is not supported by .net f-程序员宅基地

文章浏览阅读8.9k次,点赞3次,收藏8次。Character set utf8mb3 is not supported by .Net Framework 解决方法_character set 'utf8mb3' is not supported by .net framework.

Python之(18)ctypes使用-程序员宅基地

文章浏览阅读1.1k次,点赞22次,收藏25次。ctypes是一个 Python 标准库,它提供了和 C 语言库交互的能力。利用ctypes,你可以在 Python 中加载动态链接库(DLLs 或在 Unix-like 系统中的 shared objects),并且可以调用这些库中的函数。这使得Python可以使用已经编译好的代码,这通常是为了性能或者重用现有的C代码。要使用ctypes然后,你可以加载一个库,调用其中的函数,传递参数,以及获取返回值。# 对于 Windows DLL# 对于 Unix-like 系统上的 shared object。_ctypes使用

几个查期刊的网站_standard journal abbreviation (iso4)-程序员宅基地

文章浏览阅读4.9k次。(1)Ecological Indicators | Standard Journal Abbreviation (ISO4) - Academic Acceleratorhttps://academic-accelerator.com/Journal-Abbreviation/Ecological-Indicators(2)Remote Sensing in Ecology and Conservation 期刊投稿经验分享,Remote Sensing in Ecology and Conserva_standard journal abbreviation (iso4)

推荐文章

热门文章

相关标签