第四节:旋涡特效

大家好,经过前面三节课的学习,我们已经可以掌握将模型加载到游戏中进行显示,并且做出模型在水中受波光影响的效果,也学会了如何产生闪电链。这一节课,我们来学习一下如何开发旋涡特效。


在《捕鱼达人3》里,有这样一个效果,它实现了对屏幕画面进行旋转扭曲,感觉就像带着玩家通过旋涡进入了海底的世界。如图:

1407840729290243.jpg

哈哈!看起来是不是很流弊!


具体怎么实现呢?现在我们来学习一下。


要现实这样的效果主要分为三个步骤:

一、将屏幕画面渲染到一张纹理上。

二、创建一个平面网格模型,然后将第一步取得的纹理做为贴图传入一个顶点纹理坐标扭曲Shader进行渲染。

三、随着时间的增加,更新Shader中顶点纹理坐标扭曲的角度和半径。


为了方便使用,我们创建一个基于Layer的派生类,命名为VortexLayer,然后我们在其中加入一个CCRenderTexture成员指针,在初始化时创建为屏幕大小:

1
2
3
4
5
6
// 创建渲染目标纹理
auto  WinSize  = Director::getInstance()->getWinSize();
m_pTarget = CCRenderTexture::create(WinSize.width, WinSize.height, kCCTexture2DPixelFormat_RGBA8888);
m_pTarget->setClearColor(Color4F(0, 0, 0, 0));
m_pTarget->setVisible( false );
addChild(m_pTarget);


并通过它我们在draw的时候将目标Layer结点中的所有物体绘制到它的纹理中。

1
2
3
4
5
6
7
8
9
10
11
12
void  VortexLayer::draw(Renderer* renderer,  const  Mat4 &transform, uint32_t flags)
{
 
     if ( m_pTarget && m_TargetLayer)
     {
         m_pTarget->clear(0,0,0,0);
         m_pTarget->begin();
         m_TargetLayer->visit();
         m_pTarget->end();
     }
}


然后我们需要加入一个Mesh成员对象指针,并设置它的顶点数据,使它在形成一个与屏幕大小相等的平面网格。

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
auto  WinSize  = Director::getInstance()->getWinSize();
//行,列数
int  cows = 100;
int  rows = 100;
//每一行,列的顶点位置偏移
float   vertexStepX = WinSize .width/cows;
float   vertexStepY = WinSize .height/rows;
//第一行,列的纹理坐标偏移
float   uStep = 1.0/rows;
float   vStep = 1.0/cows;
//顶点,法线,色,纹理的容器
vector< float > positions;
vector< float >   normals;
vector< float >   colors;
vector< float >   texs;
 
for  ( unsigned  int  x = 0; x <= cows; ++x )
{
     for  ( unsigned  int   y = 0; y <= rows; ++y )
     {
         unsigned  int  offset = x + y * ( cows + 1 );
 
             positions.push_back(x*vertexStepX);
             positions.push_back(y*vertexStepY);
             positions.push_back(10);
 
             texs.push_back(x*uStep);
             texs.push_back(y*vStep);
         }
     }
std::vector<unsigned  short > tTriangle;
 
for  ( unsigned  int  x = 0; x < cows; ++x )
{
     for  ( unsigned  int  y = 0; y < rows; ++y )
     {
         unsigned  short * ptr = &(m_IndiceArray[(x+y*cows)*6]);
 
         tTriangle.push_back((x+0) + (y+0)*(cows+1));
         tTriangle.push_back((x+0) + (y+1)*(cows+1));
         tTriangle.push_back((x+1) + (y+0)*(cows+1));
 
         tTriangle.push_back((x+0) + (y+1)*(cows+1));
         tTriangle.push_back((x+1) + (y+1)*(cows+1));
         tTriangle.push_back((x+1) + (y+0)*(cows+1));
     }
}  
//创建模型
m_RenderMesh = Mesh::create(positions,normals,texs,tTriangle);
m_RenderMesh->retain();
//设置顶点格式
long  offset = 0;
auto attributeCount = m_RenderMesh->getMeshVertexAttribCount();
for  (auto k = 0; k < attributeCount; k++) {
        auto meshattribute = m_RenderMesh->getMeshVertexAttribute(k);
        m_ProgramState->setVertexAttribPointer(s_attributeNames[meshattribute.vertexAttrib],
                                             meshattribute.size,
                                             meshattribute.type,
                                             GL_FALSE,
                         m_RenderMesh->getVertexSizeInBytes(),
                                             (GLvoid*)offset);
        offset += meshattribute.attribSizeBytes;
    }
     
m_meshCommand.genMaterialID(0, m_ProgramState, m_RenderMesh, m_BlendFunc);


这样模型网格就创建好了,下面我们重点研究一下所需要的Shader文件Vortex.vsh:

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
// 输入的顶点格式
attribute vec4 a_position;
attribute vec2 a_texCoord;
 
//输出给PS的变量
varying vec2 v_texCoord;
//用户自定义的变量
uniform float  radius ;
uniform float  angle ;
//旋涡的计算函数
vec2 vortex( vec2 uv )
{
     //先减去贴图中心点的纹理坐标,这样是方便旋转计算
     uv -= vec2(0.5, 0.5);
     //计算当前坐标与中心点的距离。
     float  dist = length(uv);
     //计算出旋转的百分比
     float  percent = (radius - dist) / radius;
     if  ( percent < 1.0 && percent >= 0.0) 
     {
         //通过sin,cos来计算出旋转后的位置。
         float  theta = percent * percent * angle * 8.0;
         float  s =  sin (theta);
         float  c =  cos (theta);
         uv = vec2(dot(uv, vec2(c, -s)), dot(uv, vec2(s, c)));
     }
     //再加上贴图中心点的纹理坐标,这样才正确。
     uv += vec2(0.5, 0.5);
 
     return  uv;
}
 
void  main()
{
     //计算出顶点的位置
gl_Position = CC_MVPMatrix * a_position;
     //调用函数计算出纹理坐标
     v_texCoord = vortex( a_texCoord );
 
}

在draw函数中通过计算时间间隔来不断的修改角度和半径的值,就可以实现顶点的UV值绕贴图中心点旋转的效果了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//计算时间间隔
timeval currtime;
gettimeofday( &currtime, nullptr);
float  dt = (currtime.tv_sec - m_lasttime.tv_sec) + (currtime.tv_usec - m_lasttime.tv_usec) / 1000000.0f;
if  ( m_Time < m_maxTime )
{
     setAngle( getAngel()+m_angleSpeed*dt );
     setRadius( getRadius()+m_radiusSpeed*dt );
     m_Time += dt;
}
else
{
     m_Time = 0.0;
     setAngle( 0.0f );
     setRadius( 0.0f );
}
m_lasttime = currtime;
//设置Shader的参数。
GLProgramState* programstate = getGLProgramState();
programstate->setUniformFloat( "radius" ,m_radius);
programstate->setUniformFloat( "angle" ,m_angle);
programstate->setUniformTexture( "u_texture" , m_pTarget->getSprite()->getTexture());


现在我们运行一下,就可以看到这个旋涡在一点点旋转,将一切都吞噬~。 DEMO:旋涡特效.rar

1407840927235329.jpg

经过四节的学习,你是不是对于使用Cocos2d-x进行3D开发越来越有兴趣了呢?希望大家发挥聪明才智,使用Cocos2d-x开发出更多更好的3D游戏!再见!

转:http://cn.cocos2d-x.org/tutorial/show?id=1313

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值