cocos2d 2.x在opengl es 2.0 下自定义着色器来创建特别酷的特效译

cocos2d2.x在opengles2.0下自定义着色器来创建特别酷的特效(译)

(2012-12-24 13:22:17)

翻译:弹涂鱼

PS:欢迎加入开发群:285275050

本文翻译自:http://www.raywenderlich.com/10862/how-to-create-cool-effects-with-custom-shaders-in-opengl-es-2-0-and-cocos2d-2-x#

第一次翻译这么长的教程,翻译中间部分时有些不耐烦,可能大家在看时不会太明了。后面及时调整了心态。以后尽量每周翻译一篇外文资料。

自从3d游戏的引入,着色器很可能是计算机图形学的一大进步。它使编程人员可以创建全新的特效,并且能控制屏幕上所显示内容。如果你还没有用过着色器,读完此篇文章后你将会用到。

Cocos2D是当下最好ios游戏开发框架,值得庆幸的是cocos2D现在已经支持openg-es 2.0和着色器。本教程中,在Cocos2D的帮助下你将学会如何去创建和使用着色器。你将学习以下内容

lGLSL基础(opengl shader language

lCocos2D里如何创建和使用着色器

l三个着色器实例

1.在游戏中通过使用斜坡纹理(ramp textures)来操作颜色

2.创建浮雕特效

3.通过几行代码创建波浪起伏的青草

这是一篇高级教程,所以需要一些先决条件。为了能知道本篇在讲什么,你需要以下基础知识:

locios.other tutorials

lCocos2DCocos2D tutorials

lOpengl es 2.0OpenGL ES tutorials

最后,我将使用最新的xcode,请确保更新到最新版。

前言

在探索和使用着色器前,你需要cocos2d 2.x和确保xcode集成了cocos2d模板。然后创建一个cocos2d项目,步骤如下

1.下载最新cocos2d 2.x

2.解压到你喜欢的文件

3.通过控制台导航到cocos2d文件夹,运行以下命令来安装模板./install-templates.sh -u -f

4.运行xcode执行:File\New\Project…

5.选择cocos2d模板点击下一步

6.命名项目为CocosShaderEffects,选择iphone设备作为目标设备,点击下一步

7.选择工程创建的路径,点击创建

接下在你的项目中启用arc,尽管cocos2d没有使用arc,你可以在余下的项目中去启用,这可以节约代码同时能减少内存泄露。

菜单中选择Edit\Refactor\Convert to Objective-C ARC…。在代开的对话框中选中select main.m,AppDelegate.m and HelloWorldLayer.m(如果未找到点击CocosShaderEffects.app旁边的小三角展开),在接下来的一系列对话框总点击checkNextSave。在转换前你会被建议自动启用快照。本工程中你可以不启用,但是启用快照,在紧要关头可能会救了你。比如保留快照可以使你因为修改过多而而导致错误时进行回复

很简单吧?现在你可以在使用arc的同时感受cocos2d的力量。

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

编译运行,你会看到:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

由于本教程中你不会用到高清图片,如果你的程序运行在高清显示屏上,你可能想禁用高清图片加载。找到AppDelegate.m把下面代码注释掉:

if( ! [director_ enableRetinadisplay:YES] )

现在,准备工作完成,可以你的Cocos2D和着色器旅程了。

什么是着色器?

着色器是一个简单的类C程序,用于执行渲染特效。举例来说,正如名字所暗示的,着色器最基本的功能可能是为一个对象添加不同的色彩(或者是对象的一部分).

着色器程序在GPU(图形处理器)执行。对于移动设备,共有两类着色器:

1.顶点着色器:顶点着色器作用在每个正在被渲染的顶点上,所以在渲染一个简单的精灵时,着色器会执行四次用于计算精灵四个顶点的颜色和其他属性

2.片元着色器:这类着色器作用在屏幕显示的每一个像素上,这意味着当在iphone上渲染一个全屏的方形时,片源着色器会被执行320*480次。

上述两类着色器不能单独使用:它们必须同时出现。两类着色器组合在一起成为一个程序。它们这样来运行:

1.顶点着色器首先定义每个即将在屏幕上显示顶点的属性

2.然后,每个顶点依次被打散(break down in turn)成一系列像素(我理解为像素化或栅格话),这些像素通过片元着色器去执行

3.最后的像素被渲染到屏幕。

在引入着色器以前,当你渲染特效时,你只能去调用已知的固定功能管道线。固定功能管道线允许你渲染那些差不多是硬件编码的特效。你可以去更改参数(修改颜色,位置等),这些也是你唯一能做的。

固定功能管道线的种种限制促进了着色器的诞生,着色器可以使你通过编程的方式去创建新的渲染特效。通常,着色器可以实现你能想到的所有特效:着色器可以使你去编写屏幕每次绘制时调用的程序。

Opengl-ES 2着色器使用OpenGL Shading Language,GLSL来编写的,它的语法很像oc,如果你有编程经验,尤其是你读过本网站的一些文章时,你不应该会遇到问题。不用担心去阅读说明文档,我会带领你从基础开始的。

(老外写的太细致了)

如何构建在Cocos2D中工作的着色器?

Cocos2d 2.x使用Opengl-es 2.0进行图形渲染,这样,即使是最简单的渲染你都需要着色器。所以每一个CCNode节点都有一个shaderProgram实例变量,这个实例变量保留了shader program的指针,当节点绘制时会被调用

Cocos2D也提供了自己的CCshadercache允许你调用认的着色器程序,或者你也可以缓存自己的着色器程序,这样就能避免多次重复加载问题,你可以在libs\cocos2D\CCGLProgram.h找到访问预定义着色器的常量定义关键字。你可以在libs\cocos2d\ccShader_xxx.h.找到cocos2d使用的着色器。

下面这段代码一个示例着色器程序:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

在开始讲解这段代码之前,我们先来对shader有个大致了解,和它实现的目标。

每个着色器都需要输入值,并产生输出值。对于上面的着色器。

l需要每个顶点的position作为输入值,对于精灵来说就是他的四个角所对应的顶点。它也需要纹理的坐标(coordinate)作为输入值用于显示在每个顶点上(会映射纹理到四个顶点),另外还需要一个几何变换(transform)用于应用position/scale/rotate到精灵上。Cocos2d会在着色器执行前传入这些值。

l输出决定着顶点在屏幕上的最终显示坐标(输入的位置经过几何变换以后),和顶点的最终纹理坐标,片元着色器会利用这些输出变量,这些我在后面介绍。

现在你已经有了大致了解,接下来进行更详细的分步介绍:

1.定义输入顶点的数据结构,关键字attribute告诉编译器这是个包含了每个顶点数据结构的输入变量。类型vec2vec4说明数据是由浮点数组成的向量。Vec可以有2-4个组成部分,这里定义了两个,一个对应位置,另一个对应纹理坐标。

2.当你需要从你的源码中传递扩展变量到着色器时,需要用关键字uniform定义。Mat4定义了有4*4float组成的矩阵。如果你对先行代数不了解,记住矩阵是数学上表示位置,旋转,缩放的方式。

3.顶点着色器需要传递一些值到片源着色器,为了标识从顶点着色器传到片源着色器变量,使用varying作为关键字。这些变量可以通过插值去改变,很酷吧。这是一个有趣的描述方式,如果你给一个变换的坐标顶点A设置为0,变换的坐标顶点B设置为1.0,当你到达片元着色器渲染一个像素时,opengl自动将变量值设置为0.5。每一个片元都会为组成这个片元顶点进行插值计算。在这里可以看到精度标识符mediump的详细介绍。还有两类标识符:highplowp,它们定义计算数据存储空间质量(They define the importance of the quality of calculations/data storage)。Highp表示更精确的数据类型将会使用,当然计算速度相对较慢。

4.一个着色器需要有一个main方法,就像oc

5.顶点着色器需要变换顶点坐标来填充内建的变量gl_Positon,这个着色器被由cocos2d自动传入的ModelViewProjection matrix乘以了传入的Position

6.通过给变量赋值,直接传递坐标位置到片元着色器

下面是上述顶点着色器的片元着色器:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

记住当执行到片元着色器时,opengl会为组成精灵的每一个像素时调用一次这个程序。这个程序的目的是为了表示出如何去为每一个像素着色。对这个认着色器的回答很简单,仅仅是挑选出纹理中正确的点去匹配对应像素。

下面是分步介绍:

1.强制设置片元着色器顶部的基础精度为浮点型,这里设置为中等精度。

2.所有顶点着色器的输出变量,在这里设置为输入变量。顶点着色器输出了纹理坐标坐标,所以在这里纹理坐标变为输入。

3.片元着色器也可以有uniform变量,这里定义了一个uniform型的sampler2D变量,代表正常纹理。

4.gl_FragColor是内建变量,需要为它填充最后的像素颜色。这里从从uniform类型的纹理和从顶点着色器传过来的纹理坐标中获取像素。

使你明白如何和把他们组合在一起前还有许多步要走,这些纹理正好在CCGrid.m中用到了,好的,打开文件看看如何使用。

首先,在init方法shadercache中加载

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

如果你感到好奇,妮可以在CCshadercache中查看shaders是如何被编译和存储的。

接下来在blit方法中,向着色器材传入变量并运行。

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

你可能想知道纹理是在那里传入的呢?这里有个捷径如果你不设置变量,他的认值是0,另外第一个纹理的单元也是0,所以在afterDraw中绑定纹理,并且不用再次传入。

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

现在你已经学习了一个cocos2d中使用纹理的实例,你可能想去深入挖掘CCSprite是如何渲染它自己的。

当你去做那件事时,充分分析。

是时候创建属于你自己的纹理了。

如何创建自己的着色器

大多数2D游戏有精灵组成,每个精灵有四个顶点。仅仅有这些还是不够,大多数2D特效是由片元着色器创建的。你即将使用cocos2d认的顶点着色器来添加一个新的自定义片元着色器。

这里的目的是创建一个简单效果在这里你将使用第二纹理去修改原始纹理颜色。你将使用“ramp”纹理(“ramp”texture)来操纵原始纹理。这是一个可以用与创建皮肤或卡通着色的效果

下面是我们将要用到的纹理:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

我们将处理颜色的三部分(rgb),它们被表述成0-1的百分比,如果你在使用0-255的范围表示,不要忘记除以255.

注意图片是如何从白(RGB 0.0,0.0,0.0)变到黑的(RGB 1.0,1.0,1.0)。对于原始图片中的rgb值,我们去寻找这个颜色遮罩的相关入口,有效的反转原始图片的颜色值。比如:

(下面的不翻译了,相信你明白他在说什么)

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

所以,由于上图只有64像素宽,相对于原始图像它有较小的颜色变化范围,这导致了带状果。

好吧,让我们来试一下。你可能刚刚运行程序时就已经注意到,现在你有两个菜单选项--Achievementsleaderboards。由于不需要Achievementsleaderboards来处理纹理,所以你需要修改一下程序菜单,使它导航到着色器测试界面。

创建一个继承自cclayer类,命名为CSEColorRamp

打开HelloWorldLayer.m引入CSEColorRamp的头文件

#import "CSEColorRamp.h"

替换HelloWorldLayerinit方法为:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

编译运行,你将看到

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

点击Color Ramp将导航到一个黑色界面,我们来让它变得有趣。

打开CSEColorRamp替换@implementation只有这一行为

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

我们只是添加了一些私有变量。

1.你想改变颜色的精灵

2.为了传递数据给着色器中的uniform变量,我们需要创建一个变量来跟踪unifromd的位置

3.你的ramp纹理

替换init方法为:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

你都做了些什么?

1.加载精灵,使他可以铺满屏幕

2.强制精灵使用你自己的着色器程序。它将使用自己认的顶点着色器ccPositionTextureA8Color_vert自定义的片元着色器CSEColorRamp.fsh,然后标示你的顶点将包含位置和纹理坐标属性。然后链接着色器并cocos2Duiniforms

3.着色器连接完成后,你可以询问openglu_colorRampTexture标识的第二纹理的硬件位置。然后初始化这个位置为1.(我们会在slot 1存储纹理,slot 0是精灵的纹理)

4.加载你的渐变纹理(ramp texture)并禁用线性插值,这里我们保留原始值。

5.绑定纹理,设置第二纹理为你的ramp texture(注意在实际项目中,在精灵的每一次绘制你都要设置一下这些值,因为其他渲染可能会改变这些值),这样就绑定你的纹理到u_colorRampTexture上了。

现在需要片元着色器来使这个层来工作。

创建一个新的空文件iOS\Other\Empty template,命名为CSEColorRamp.fsh,选择文件位置,将CocosShaderEffectstargets列表中移除点击create.移除的原因是它不是源码文件。而是资源(Xcode by default would add it to the Compile Sources phase).

现在你需要将新添加的片元着色器添加copy Bundle Resources build phase(省略详细)

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

修改CSEColorRamp.fsh如下:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

注意:为增加上代码可读性,在菜单中选择Editor/Syntax Coloring/GLSL,将会开始着色器语法颜色显示

上述代码做了些什么?

1.此着色器需要从顶点着色器传来的纹理坐标,和从程序中传来的两个纹理

2.获取纹理原始值

3.使用真实的通道值作为地址去获取ramp texture修改后的颜色。

4.创建基于简便的片元颜色,并使他不透明。

运行程序你会看到:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

Cocos2d只是改变了他的皮肤颜色。

你可以尝试改变绑定的颜色图片来达到不同效果。减小图片宽度,将会有较小的颜色范围,这可以用于卡通着色。

创建浮雕着色器

是时候创建新的着色器了。

1.创建新类CSEemboss(同上)

2.helloworldLayer修改init方法

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下<a href=自定义着色器来创建特别酷的特效(译)" title="cocos2d2.x在opengles2.0自定义着色器来创建特别酷的特效(译)" style="margin:0px; padding:0px; border:0px; list-style:none; line-height:1.5" src="http://s8.sinaimg.cn/mw690/6084f588td19248fd2d17&690">

3.添加一个着色器CSEemboss.fsh添加bundle中(同上)

修该CSEemboss.fsh如下

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)


上述代码实现了什么:

1.此着色器需要从顶点着色器传来的纹理坐标,和从程序中传来的两个纹理

2.定义纹理坐标空间所关联纹理的每个像素的尺寸

3.复制纹理坐标(在下一节中你会知道原因)

4.定义基础颜色,为白色的一般(which will be half the power of whitepower为幂,在这不知如何翻译合适),然后减去每个像素斜向运动的颜色,并添加相反的斜向运动位移。这会使像素处于不同的颜色边框并站立。相同颜色的像素会与基础颜色更接近。

5.对颜色平均,这样你会的到在原始纹理中关注于不同颜色的灰度图。

运行程序:


cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)


添加动作

那确实很cool,那么我们给这个浮雕效果添加一些动作怎么样?

打开CSEemboss.m,移除与ramp texture有关的两个变量,

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

你需要uniform location添加以下几行到init方法中(替换当前的3-5

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

上述代码获取了时间值的uniform location,并且加入到cocos2dupdate循环(每帧需要调用函数

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

上述代码每次更新时增加totalTime的值,并把它传给你自定义的着色器。调用时使用gluniform1f,因为你只传递了一个float值。

现在来修改一下你的CSEemboss.fsh着色器,以便使用传入的uiniform类型的u_time。为了使用这个值来进行移动,你可以使用的最简单函数是基于u_timesine/cosine。替换#3部分的代码为:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

新的代码通过改变由texCoords所标识的坐标偏移来创建一个-6+6变化的波浪动作。

编译运行,选择emboss menu你会看到一个浮雕化的cocos2d图标在移动。

简单动态的草

在最后,你将使用着色器来创建一些简单动态的草,你可以把它用到你的cocos2d 2.x游戏中。

使用iOS\cocos2d v2.x\CCNode模板类创建一个新的文件,使其继承cclayer并命名为CSEGrass.m(如果你也复制@implementation这一行,不要忘记更改类名),在init方法中改变加载的片元着色器文件名为CSEGrass.fsh

替换#1部分的代码为:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

我们把代码改为使用草的纹理,由于草的纹理已经是横向状态,所以我们把rotation去掉。

接下来打开HelloWorldLayer.m并引入CSEGrass.h文件:

#import "CSEGrass.h"

添加一个新的菜单项:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

添加一个新的片元着色器CSEGrass.fsh添加步骤同上。

修改片元着色器为:


cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

新的着色器是这样工作的以使草向一方弯曲,然后向另一方弯曲。这个弯曲影响操的顶部,而不是整个纹理。

以下是每一步的说明:

1.定义一些常量以便于修改特效。

2.基于高度去计算纹理的偏移。纹理在顶部从0开始,所以你需要反转一下。

3.你不希望使用高度值去影响线性弯曲,正如你所希望纹理底部保持直立。弯曲程度应该随着草的高度而增加,所以这里最合适的方法是基于高度的指数去计算弯曲。

4.提供弯曲的最简单方法调用sine函数。将频率乘以速度常量,弯曲范围随sine对应的值与bendFactor的乘积而改变

5.offset偏移量加到texCoord.x上,函数fract可以确保当你获取的值小于0或大于1时,纹理会重复。然后只用偏移颜色作为你最终的片元颜色。

编译运行程序:

cocos2d<wbr></p>2.x在opengl<wbr>es<wbr>2.0<wbr>下自定义着色器来创建特别酷的特效(译)

翻译到此为止吧,后面还有一点无关紧要的,有兴趣的可以自己看英文的。

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...