(译)如何使用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和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF