(译)如何使用cocos2d 2.0 (安装)来给一个Sprite添加遮罩

2024-01-29 11:48

本文主要是介绍(译)如何使用cocos2d 2.0 (安装)来给一个Sprite添加遮罩,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(译)如何使用cocos2d 2.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;

attribute vec4 a_color;

uniform mat4 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;
#endifvarying 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;
#endifvarying 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翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!


这篇关于(译)如何使用cocos2d 2.0 (安装)来给一个Sprite添加遮罩的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/656803

相关文章

Python使用Pandas对比两列数据取最大值的五种方法

《Python使用Pandas对比两列数据取最大值的五种方法》本文主要介绍使用Pandas对比两列数据取最大值的五种方法,包括使用max方法、apply方法结合lambda函数、函数、clip方法、w... 目录引言一、使用max方法二、使用apply方法结合lambda函数三、使用np.maximum函数

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本