《捕鱼达人》教程4:漩涡特效
经过前面三课的学习,我们已经掌握了如何将模型加载到游戏中进行显示,实现模型在水中受波光影响的效果,同时也学会了闪电链的产生方法。在这节课中,我们将学习如何开发旋涡特效。
玩过《捕鱼达人3》的朋友应该记得,游戏中有一个对屏幕画面进行旋转扭曲的效果,仿佛带着玩家通过旋涡进入了海底世界,如下图所示:
看起来是不是很炫酷!接下来,我们就来学习具体的实现方法。
要实现这样的效果,主要分为以下三个步骤:
- 将屏幕画面渲染到一张纹理上。
- 创建一个平面网格模型,然后将第一步取得的纹理作为贴图传入一个顶点纹理坐标扭曲Shader进行渲染。
- 随着时间的增加,更新Shader中顶点纹理坐标扭曲的角度和半径。
为了方便使用,我们创建一个基于Layer的派生类,命名为VortexLayer。在这个类中,我们加入一个CCRenderTexture成员指针,并在初始化时将其创建为屏幕大小,具体代码如下:
// 创建渲染目标纹理
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结点中的所有物体绘制到该纹理中,代码如下:
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成员对象指针,并设置它的顶点数据,使其形成一个与屏幕大小相等的平面网格。具体代码如下:
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;
// 顶点,法线,色,纹理的容器
std::vector<float> positions;
std::vector<float> normals;
std::vector<float> colors;
std::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,代码如下:
// 输入的顶点格式
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);
// 计算出旋转的百分比
// 后续代码未完整给出,可根据实际需求补充
...
}
在上述Shader代码中,我们定义了输入的顶点格式、输出给PS的变量以及用户自定义的变量,并实现了旋涡的计算函数。通过这个Shader文件,我们可以实现对纹理坐标的扭曲,从而达到旋涡特效的效果。
综上所述,我们通过将屏幕画面渲染到纹理上、创建平面网格模型并设置顶点数据以及编写Shader文件,实现了《捕鱼达人3》中的旋涡特效。希望本教程对你有所帮助。