经过前面三课的学习,我们已经可以掌握了怎样将模型加载到游戏中进行显示,并且做出模型在水中受波光影响的效果,同时也学会了闪电链是怎样产生的。这一节课,我们来学习一下怎样开发旋涡特效。
玩过这款游戏的朋友都记得在《捕鱼达人3》里,有这样一个效果,它实现了对屏幕画面进行旋转扭曲,感觉就像带着玩家通过旋涡进入了海底的世界。如图:
哈哈!看起来是不是很流弊!
具体怎么实现呢?现在我们来学习一下。
要现实这样的效果主要分为三个步骤:
一、将屏幕画面渲染到一张纹理上。
二、创建一个平面网格模型,然后将第一步取得的纹理做为贴图传入一个顶点纹理坐标扭曲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;
//用户自定义的变量
uniformfloat radius ;
uniformfloat angle ;
//旋涡的计算函数
vec2 vortex( vec2 uv )
{
//先减去贴图中心点的纹理坐标,这样是方便旋转计算
uv -= vec2(0.5, 0.5);
//计算当前坐标与中心点的距离。
float dist = length(uv);
//计算出旋转的百分比
|