热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(译)如何使用cocos2d2.0来给一个Sprite添加遮罩

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢

  免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

原文链接地址:http://www.raywenderlich.com/4428/how-to-mask-a-sprite-with-cocos2d-2-0

教程截图:

  在上一篇教程中,我们向大家展示了如何使用cocos2d 1.0来给精灵增加mask。

  那种方法很不错,但是,也有一些缺点----它会使纹理占用内存增加,同时在渲染的时候还有性能问题。

  但是,通过使用Cocos2D 2.0和OpenGL ES 2.0,我们可以用shader来更高效地实现精灵的mask。

  这篇教程是我们第一次尝试使用cocos2d 2.0分支。你将从这里学习到,如何下载cocos2d 2.0分支,如何为一个CCNode写自己的shader程序。

  为了更好地理解本教程里的内容,你需要有一些基本的OpenGL ES 2.0的知识。所以,如果你对OpenGL ES 2.0还很陌生的话,你可以先看看本博客翻译的其它OpenGL ES 2.0教程。

  话不多说,让我们使用shader来制作mask吧。

 

Introducing Cocos2D 2.0 Branch

  Cocos2D 2.0是一個全新的版本,它使用的是OpenGL ES 2.0,而之前的版本使用的是OpenGL ES 1.0. 这意味着,那些比较老的设备,如果不支持OpenGL ES 2.0的话,那么就不能再跑使用Cocos2D 2.0制作的程序了。但是,目前,大部分设备都是支持opengles 2.0的,因此,转向opengles 2.0是大势所趋。

  因为,即使一些老的设备不支持opengles 2.0,但是,玩一玩opengles 2.0的shader程序还是很有意思的!而且,你会看到,使用shader效率会更高。

  Cocos2d 2.0分支还处在开发阶段,但是,现在,我们可以下载抢鲜版来玩一玩。

  获得cocos2d 2.0的唯一方式,就是通过git仓库,因此,如果你还没安装git的话,那么可以从这里下载。(译者:关于git,它是和svn同类型的源码管理工具,但是,比svn更好用,更强大,支持分布式源码管理。之前博客园的首页有人发表过文章,现在,如果你还没用上git的,你都不好意思说自己是程序员。)

  然后,打开Applications\Utilities目录,打开Terminal程序并打开。然后在终端里面输入下面的git命令:

git clone https://github.com/cocos2d/cocos2d-iphone.git

  一旦你完成下载,然后就可以跳转到cocos2d-iphone的目录了,然后可以把cocos2d 2.0的会支checkout出来。具体命令如下:(这个下载过程刚开始有点长,因为它要把远程的代码仓库下载到本地,以后cocos2d有什么更新,你只需要git pull 就可以了。比你每次下载新的版本要快得多。如果大家对git还不熟悉的话,可以google一下,progit,去这个网站学习一下吧,绝对值得!)  

cd cocos2d-iphone
git checkout gles20

  接下来,要安装cocos2d 2.0的模板。这样的话,会把之前你安装的cocos2d 1.0的模板覆盖掉,那是没关系的---你只需要再choutout回原来的1.0版本,然后再运行一次install-template.sh就可以了。你也可以保存两个cocos2d版本,先把cocos2d 2.0的版本复制到另外的地方,然后checkout到1.0。  这样你想安装1.0就安装1.0,想安装2.0就安装2.0)  

./install-templates.sh -f -u

  然后回到xcode,选择File\New\New Project,再选择 iOS\cocos2d\cocos2d,点Next。把工程命名为MaskedCal2,再点Next,选择一个文件夹来保存工程,然后点Create。

    如果你现在编译并运行,你会看到一个HelloWorld!(译者:Ray写本文的时候模板有点问题,运行之后是黑屏,不过现在的版本已经解决这个问题了。Ray给出的解决方案是把下载的cocos2d 目录下的shaders全部copy进工程,不过,对于我们来说,现在不必要了。不过我还是在这里说明一下。如果没有出现“Hello World”,那么就把shader copy进来吧)。


创建一个简单的cocos2d 2.0工程

  首先,和之前一样,让我们先在屏幕上面显示一张日历图片。

  和之前一样,你把下载好的资源文件拖到Resource目录中,然后确保“Copy items into destination group’s folder (if needed)”被选中,而且 “Create groups for any added folders”也要被选中,然后点击Finish。

  然后打开AppDelegate.m,并作如下修改:

// Add to top of file
#import "SimpleAudioEngine.h"

// At end of applicationDidFinishLaunching, replace last line with the following 2 lines:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"TeaRoots.mp3" loop:YES];
[[CCDirector sharedDirector] runWithScene: [HelloWorldLayer sceneWithLastCalendar:
0]];

  这里和上一篇教程中一样。  

      然后打开RootViewController.m,找到shouldAutorotateToInterfaceOrientation方法,然后返回下面的语句:

return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) );

    这个设置是使设备仅支持横版(landscape)模式。

    接下来,打开HelloWorldLayer.h,然后作如下修改:  

// Add new instance variable
int calendarNum;

// Replace the +(CCScene*) scene declaration at the bottom with the following:
+ (CCScene *) sceneWithLastCalendar:(int)lastCalendar;
- (id)initWithLastCalendar:(int)lastCalendar;

    这里也和上一篇教程中的一样。

    最后,我们在 HelloWorldLayer.m里面作如下修改:

// Replace +(CCScene *) scene with the following
+(CCScene *) sceneWithLastCalendar:(int)lastCalendar // new
{
CCScene
*scene = [CCScene node];
HelloWorldLayer
*layer = [[[HelloWorldLayer alloc]
initWithLastCalendar:lastCalendar] autorelease];
// new
[scene addChild: layer];
return scene;
}

// Replace init with the following
-(id) initWithLastCalendar:(int)lastCalendar
{
if( (self=[super init])) {

CGSize winSize
= [CCDirector sharedDirector].winSize;

do {
calendarNum
= arc4random() %3+1;
}
while (calendarNum == lastCalendar);

NSString
* spriteName = [NSString
stringWithFormat:
@"Calendar%d.png", calendarNum];

// BEGINTEMP
CCSprite * cal = [CCSprite spriteWithFile:spriteName];
cal.position
= ccp(winSize.width/2, winSize.height/2);
[self addChild:cal];
// ENDTEMP

self.isTouchEnabled
= YES;
}
return self;
}

// Add new methods
- (void)registerWithTouchDispatcher {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:
0 swallowsTouches:YES];
}

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CCScene
*scene = [HelloWorldLayer sceneWithLastCalendar:calendarNum];
[[CCDirector sharedDirector] replaceScene:
[CCTransitionJumpZoom transitionWithDuration:
1.0 scene:scene]];
return TRUE;
}

    还是和上一篇教程一样,我们等下要把(begintemp和endtemp之间的代码替换掉)。因此,你可以看到,其实cocos2d 2.0的api和1.0的api非常类似--当然,接下来你会看到2.0的一些非常酷的特性。  

    编译并运行,然后你可以点击模拟器,那么每次都会显示一张不同的日历,这和之前使用cocos2d 1.0没有什么不同。但是,接下来,我们将使用OpenGL ES 2.0和cocos2d 2.0来制作mask,准备好了吗?

Shaders and Cocos2D 2.0

    cocos2d 2.0有许多内置的shader程序。

    让我们先看一看,一个普通的CCSprite渲染的shader程序是怎么样的吧。他们其实非常简单,打开Shaders\PositionTextureColor.vsh,如下图所示:

 
 

attribute vec4 a_position;

 
 

attribute vec2 a_texCoord;

 
 

attributevec4 a_color;

 
 

uniformmat4 u_MVPMatrix;

 
 

varying vec4 v_fragmentColor;

 
 

varying vec2 v_texCoord;

 
 

void main()

 
 

{

 
 

    gl_Position = u_MVPMatrix * a_position;

 
 

    v_fragmentColor = a_color;

 
 

    v_texCoord = a_texCoord;

 
 

}

 

    这里把传进来的顶点坐标乘以投影矩阵和模型视图矩阵(即u_MVPMatrix矩阵,它是projection matrix 和model/view matrix的乘积),然后把输入的片断颜色和纹理坐标传递给相应的输出参数。

    接下来,让我们看看Shaders\PositionTextureColor.fsh: 


#ifdef GL_ES
precision lowp
float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D u_texture;

void main()
{
gl_FragColor
= v_fragmentColor * texture2D(u_texture, v_texCoord);
}

    这里把输出颜色等于纹理颜色乘以顶点颜色。  

    cocos2d 2.0里面,你可以为每个node编写定制的shader程序来渲染它。接下来,让我们为sprite类写一段mask shader吧。

    回到Xcode,选择File\New\New file,再选择 iOS\Other\Empty,点击Next。然后命名为Mask.fsh并点击Save。

    然后,点工程,选择MaskedCal2 target(如下图红色圈圈所示),然后选择 Build Phase标签,在”Compile Sources“里面找到Mask.fsh,并把它拖到”Copy Bundle Resources“里面。这样会把shader程序拷贝到你的应用程序bundle里面,而不会直接参与源文件的编译。

  (顺便提一下,如果谁有更简单的方法,请留言告诉我)

    然后把Mask.fsh里面的内容替换成下面的形式:

#ifdef GL_ES
precision lowp
float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform sampler2D u_mask;

void main()
{
vec4 texColor
= texture2D(u_texture, v_texCoord);
vec4 maskColor
= texture2D(u_mask, v_texCoord);
vec4 finalColor
= vec4(texColor.r, texColor.g, texColor.b, maskColor.a * texColor.a);
gl_FragColor
= v_fragmentColor * finalColor;
}

 这里,我们为mask纹理建立了一个新的uniform,然后把日历和mask纹理里的像素读取到uniform变量中来。

 然后,我们通过日历的RGB值来构造最终的颜色值,但是把alpha通道乘以mask的alpha通道。这样,就可以制作出遮罩效果了来啦。

   好了,现在我们有shader程序啦,让我们来使用它们吧!

在cocos2d 2.0里面使用定制的shader

    为了使用定制的shader,你需要创建一个CCNode的子类,设置它的shaderProgram为你的定制的shader,而且还需要覆盖其draw方法来给shader程序传递合适的参数。

    我们接下来将创建一个CCSprite的子类(因为CCSprite也是继承的CCNode),把这个类命名为MaskedSprite。

   选择File\New\New File,然后选择iOS\Cocoa Touch\Objective-C class,再点击Next,然后输入CCSprite为subclass,接着,点Next,把新的文件命名为MaskedSprite.m,最后点击Save。

    然后把MaskedSprite.h替换成下面的内容:

#import "cocos2d.h"

@interface MaskedSprite : CCSprite {
CCTexture2D
* _maskTexture;
GLuint _textureLocation;
GLuint _maskLocation;
}

@end

    这里我们定义了一个实例变量来追踪mask纹理,还有两个实例变量用来追踪纹理的uniform位置,和mask 的uniform位置。  

    接下,我们回到MaskedSprite.m,然后把里面的内容替换如下:

#import "MaskedSprite.h"



@implementation MaskedSprite

- (id)initWithFile:(NSString *)file
{
self
= [super initWithFile:file];
if (self) {

// 1
_maskTexture = [[[CCTextureCache sharedTextureCache] addImage:@"CalendarMask.png"] retain];

// 2
self.shaderProgram =
[[[GLProgram alloc]
initWithVertexShaderFilename:
@"PositionTextureColor.vsh"
fragmentShaderFilename:
@"Mask.fsh"] autorelease];

CHECK_GL_ERROR_DEBUG();

// 3
[shaderProgram_ addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position];
[shaderProgram_ addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color];
[shaderProgram_ addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords];

CHECK_GL_ERROR_DEBUG();

// 4
[shaderProgram_ link];

CHECK_GL_ERROR_DEBUG();

// 5
[shaderProgram_ updateUniforms];

CHECK_GL_ERROR_DEBUG();

// 6
_textureLocation = glGetUniformLocation( shaderProgram_->program_, "u_texture");
_maskLocation
= glGetUniformLocation( shaderProgram_->program_, "u_mask");

CHECK_GL_ERROR_DEBUG();

}

return self;
}
@end

  这里有许多内容有待讨论,让我们一步步来看吧。

  1. 获得日历mask纹理的引用.
  2. 给CCNode内置的shaderProgram属性赋值,这样的话,你就可以使用自己的顶点和片断shader了。我们使用内置的PositionTextureColor顶点shader(因为并不需要改oyc什么),然后使用新的Mask.fsh来作为片断shader。注意,这里的GLProgram类和Jeff LaMarche的blog是一样的。
  3. 在链接之前,为每一个属性设置index。在OpenGL ES 2.0里面,你可以像本文中的一样,手动指定每个属性的index。也可以像之前我们在《Opengl es 2.0 iphone开发指引》中所使用的方法。
  4. 调用shaderProgram来链接到编译器和shaders。
  5. 调用shaderProgram的updateUniforms 方法,这是cocos2d 2.0特有的一个非常重要的方法。回想一下,还记得shaders里面的那些projection和model/view uiniform 吗?这个方法的调用,可以把那些uiniform都存到一个字典里面,因此,cocos2d就可以自动地基于当前的节点来设置postion和变换(transform)。
  6. 获得纹理和mask的uniform,我们后面将用到。

    接下来,我们将不得不覆盖些类的draw方法,用以给shader传递合适的参数。所以,在init方法后面加上下面的代码:

-(void) draw {    

// 1
ccGLBlendFunc( blendFunc_.src, blendFunc_.dst );
ccGLUseProgram( shaderProgram_
->program_ );

// ccGLUniformProjectionMatrix( shaderProgram_ );
// ccGLUniformModelViewMatrix( shaderProgram_ );
ccGLUniformModelViewProjectionMatrix(shaderProgram_);

// 2
glActiveTexture(GL_TEXTURE0);
glBindTexture( GL_TEXTURE_2D, [texture_ name] );
glUniform1i(_textureLocation,
0);

glActiveTexture(GL_TEXTURE1);
glBindTexture( GL_TEXTURE_2D, [_maskTexture name] );
glUniform1i(_maskLocation,
1);

// 3
#define kQuadSize sizeof(quad_.bl)
long offset = (long)&quad_;

// vertex
NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
glVertexAttribPointer(kCCVertexAttrib_Position,
3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));

// texCoods
diff = offsetof( ccV3F_C4B_T2F, texCoords);
glVertexAttribPointer(kCCVertexAttrib_TexCoords,
2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));

// color
diff = offsetof( ccV3F_C4B_T2F, colors);
glVertexAttribPointer(kCCVertexAttrib_Color,
4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));

// 4
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glActiveTexture(GL_TEXTURE0);
}

  让我们一步步看这些代码是如何工作的:

  1. 这是一段万金油代码。它设置此node的blend函数,使用shader 程序,并且设置projection和model/view uniform。
  2. 这里把日历纹理绑定到1号纹理单元,把mask纹理绑定到2号纹理单元。在《Opengl Es 2.0 iphone开发指引:第二部分,纹理贴图》有讨论。
  3. CCSprite已经包含代码来设置顶点,颜色和映射纹理坐标了--它把这些东西存储在一个特殊的结构里面,叫做quad。这里使用quad计算偏移,然后设置相应的顶点,颜色和纹理坐标。
  4. 最后,我们通过调用GL_TRIANGLE_STRIP 来渲染精灵,并且重新激活0号texture单元。(否则的话,1号纹理单元就会处于激活状态,而cocos2d假设的是0号纹理单元处于激活状态)。

    就快完成啦!回到HelloWorldLayer.m,然后作如下修改:

// Add to top of file
#import "MaskedSprite.h"

// Replace code between BEGINTEMP and ENDTEMP with the following
MaskedSprite * maskedCal = [MaskedSprite spriteWithFile:spriteName];
maskedCal.position
= ccp(winSize.width/2, winSize.height/2);
[self addChild:maskedCal];

    就这么多!编译并运行,现在你可以看到使用cocos2d 2.0和opengles 2.0制作的mask效果啦!  

    这里最牛叉的地方就再于,我们并没有创建任何额外的纹理,而且shader的效率也非常高!

何去何从?

    这里有本教程的完整源代码。(大家如果下载源代码,会发现代码和我这里给出来的不一样。为什么呢?因为cocos2d 2.0在不断地发展之中,Ray写作这篇教程时的代码,用于新的cocos2d 2.0已经不适合了。但是,如果你下载Ray提供的源代码还是能运行的。)

    至此,本系列教程就全部结束了。你可以使用cocos2d 1.0和2.0来给sprite增加mask,it's up to you!

   如果你想学习更多有关cocos2d 2.0的知识,你可以玩一玩里面的shaderTest。

   如果你想学习更多关于shader的内容,推荐看Philip Rideout’s iPhone 3D Programming--Ray也是看这本书学习的。

  如果大家实践本教程的过程中遇到任何问题,请留言!


 

著作权声明:本文由http://www.cnblogs.com/andyque翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

 


推荐阅读
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 随着网络安全威胁的不断演变,电子邮件系统成为攻击者频繁利用的目标。本文详细探讨了电子邮件系统中的常见漏洞及其潜在风险,并提供了专业的防护建议。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 本文探讨了在Git子模块目录中运行pre-commit时遇到的错误,并提供了一种通过Docker环境解决此问题的方法。 ... [详细]
  • This pull request aims to optimize the npm install retry time in branch 0.7, reducing delays caused by long timeouts when no network connection is available. ... [详细]
  • 本文总结了多个常用的 Git 命令,包括分支管理、代码提交、远程仓库同步等,旨在帮助开发者高效利用 Git 进行版本控制。 ... [详细]
  • 在本周的白板演练中,Apache Flink 的 PMC 成员及数据工匠首席技术官 Stephan Ewen 深入探讨了如何利用保存点功能进行流处理中的数据重新处理、错误修复、系统升级和 A/B 测试。本文将详细解释保存点的工作原理及其应用场景。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 本文详细介绍了网络存储技术的基本概念、分类及应用场景。通过分析直连式存储(DAS)、网络附加存储(NAS)和存储区域网络(SAN)的特点,帮助读者理解不同存储方式的优势与局限性。 ... [详细]
  • 本文提供了 Xcode 12.0 和 12.1(版本号 16B91)开发工具包的下载链接及安装步骤。通过 Finder 和快捷键,您可以轻松访问和配置 DeviceSupport 文件夹,确保 Xcode 正常运行。 ... [详细]
  • 本文探讨了Java编程的核心要素,特别是其面向对象的特性,并详细介绍了Java虚拟机、类装载器体系结构、Java类文件和Java API等关键技术。这些技术使得Java成为一种功能强大且易于使用的编程语言。 ... [详细]
  • 全面解析运维监控:白盒与黑盒监控及四大黄金指标
    本文深入探讨了白盒和黑盒监控的概念,以及它们在系统监控中的应用。通过详细分析基础监控和业务监控的不同采集方法,结合四个黄金指标的解读,帮助读者更好地理解和实施有效的监控策略。 ... [详细]
  • 深入解析Serverless架构模式
    本文将详细介绍Serverless架构模式的核心概念、工作原理及其优势。通过对比传统架构,探讨Serverless如何简化应用开发与运维流程,并介绍当前主流的Serverless平台。 ... [详细]
  • 盈透证券提现指南
    本文详细介绍了如何通过盈透证券平台安全有效地进行资金提现,包括推荐使用的浏览器、登录流程、资金转账的具体步骤以及注意事项。 ... [详细]
  • 假设您当前位于主分支(版本C4),但发现某些功能已损坏,而这些功能在之前的版本C1时还能正常工作。如何将从C1到C4的所有提交复制到一个新的特性分支中,同时将主分支回滚至C1,以便能够安全地进行版本控制和持续集成部署? ... [详细]
author-avatar
小鱼2502907687
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有