移植:在Cocos2d-X中实现水波滤镜

因为工作原因,开始转向cocos2d-x开发方向了。
自然的,凭着之前引擎的经验偏向底层渲染研究。

在此期间看了两本书
《cocos2d-x 权威指南》
《cocos2d-x 高级开发教程》
不得不说,第一本书完全是API介绍,也许适合新手入门,但是于我,只是烂书一本。
第二本书还是不错的,最后的项目有全套源码,教程的风格也很有我的风格,哈哈,就是文章中不一定贴全代码,只贴核心,后期还要自己领会补全。

言归正传,在 《高级开发教程》 中有一篇是实现海底水纹效果的文章,里面代码不全,且代码和Shader并没有对应起来,于是我决定按照自己思路来将其补全,并贴上完整源码。

先上效果图:
waveCover
动态GIF图地址: http://pan.baidu.com/share/link?shareid=1858640040&uk=3221530571

首先创建一个 ShaderNode.h 类并继承CCNode

#ifndef __FishingJoy_ShaderNode__
#define __FishingJoy_ShaderNode__
#include "cocos2d.h"
USING_NS_CC;
class ShaderNode : public CCNode
{
public:
    ShaderNode();
    bool initWithVertex(const char *vert, const char *frag);
    void loadShaderVertex(const char *vert, const char *frag);
    virtual void update(float delta);
    virtual void setContentSize(const CCSize& var);
    virtual void setColor(ccColor4F newColor);
    virtual void draw();
    static ShaderNode* shaderNodeWithVertex(const char *vert,
        const char *frag);

private:
    GLuint      m_uniformResolution, m_uniformTime, m_uniformTex0;
    GLuint      m_attributeColor, m_attributePosition;
    float       m_time;
    ccVertex2F  m_resolution,m_center;
    GLuint      m_texture;
    GLfloat     color[4];
};
#endif

之后创建ShaderNode.cpp 并实现 ShaderNode.h 中的各方法

#include "ShaderNode.h"

ShaderNode::ShaderNode()
{

}

ShaderNode* ShaderNode::shaderNodeWithVertex(const char *vert, const char *frag)
{
    ShaderNode* shader = new ShaderNode();
    if(shader && shader->initWithVertex(vert,frag))
    {
        shader->autorelease();
        return shader;
    }

    CC_SAFE_DELETE(shader);
    return NULL;
}

void ShaderNode::loadShaderVertex(const char *vert, const char *frag)
{
    CCGLProgram* shader = new CCGLProgram();  
    shader->initWithVertexShaderFilename(vert, frag);   //载入着色器程序  

    //绑定attribute变量  
    shader->addAttribute("a_position", 0);  
    shader->addAttribute("a_color", 1);  
    shader->link();  

    //获取attribute变量标识  
    m_attributeColor = glGetAttribLocation(shader->getProgram(), "a_color");  
    m_attributePosition = glGetAttribLocation(  
        shader->getProgram(), "a_position");  
    shader->updateUniforms();  

    //获取uniform变量标识  
    m_uniformResolution = glGetUniformLocation(shader->getProgram(), "resolution");  
    m_uniformTime = glGetUniformLocation(shader->getProgram(), "time");  
    m_uniformTex0 = glGetUniformLocation(shader->getProgram(), "tex0");  

    //使用着色器程序  
    this->setShaderProgram(shader);  
    shader->release();  
}

void ShaderNode::setColor(ccColor4F newColor)
{
    color[0] = newColor.r;
    color[1] = newColor.g;
    color[2] = newColor.b;
    color[3] = newColor.a;
}

bool ShaderNode::initWithVertex(const char *vert, const char *frag)
{
    loadShaderVertex(vert,frag);
    m_texture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png")->getName();

    setContentSize(CCSizeMake(1024,768));
    setColor(ccc4f(0.5,0.5,1,1));
    m_time = 0;
    scheduleUpdate();
    return true;
}

void ShaderNode::update(float dt)
{
    m_time += dt;
}

void ShaderNode::setContentSize(const CCSize& var)
{
    CCNode::setContentSize(var);
    m_resolution = vertex2(getContentSize().width,getContentSize().height);
    m_center.x = m_resolution.x/2;
    m_center.y = m_resolution.y/2;
}

void ShaderNode::draw()
{
    CC_NODE_DRAW_SETUP();  

    //传递uniform变量  
    CCGLProgram* shader = getShaderProgram();  
    shader->setUniformLocationWith2f(m_uniformResolution, m_resolution.x,  
    m_resolution.y);
    shader->setUniformLocationWith1i(m_uniformTex0, 0);
    glUniform1f(m_uniformTime, m_time);

    //获取attribute变量  
    CCSize size = this->getContentSize();  
    float w = size.width;  
    float h = size.height;  

    ccGLBindTexture2D(m_texture);               //绑定纹理到槽位  
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, w, h, 0); //截取屏幕数据到纹理  
    glEnableVertexAttribArray(m_attributePosition);  
    glDisableVertexAttribArray(m_attributeColor);  

    //传递attribute变量  
    GLfloat vertices[12] = {  
        0, 0, //左下0
        w, 0, //右下1
        w, h, //右上2
        0, 0, //左下0
        0, h, //左上3
        w, h, //右上2
    };  
    glVertexAttribPointer(m_attributePosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);  
    glVertexAttrib4fv(m_attributeColor, color);  

    //绘制  
    glDrawArrays(GL_TRIANGLES, 0, 6);  
}

之后在场景类中添加ShaderNode 即可

        ShaderNode* shader = ShaderNode::shaderNodeWithVertex("shader.vsh","shader.fsh");
        shader->setContentSize(getContentSize());
        shader->setColor(ccc4f(1,1,1.0,.5));
        this->addChild(shader,2);

shader.vsh 和 shader.fsh 可以从网络中得到,就是glsl文件
shader.vsh 的内容为

attribute vec4 a_color;  
attribute vec4 a_position;  
varying vec4 v_color;  
uniform mat4 u_MVPMatrix;  
void main()  
{  
    v_color = a_color;  
    gl_Position = u_MVPMatrix * a_position;  
}

shader.fsh 的内容为

varying vec4 v_color;  
 
uniform sampler2D tex0;  
precision highp float;  
uniform float time;  
uniform vec2 resolution;  
const float PI = 3.1415926535897932;  

const float speed = 0.2;  
const float speed_x = 0.3;  
const float speed_y = 0.3;  
 
const float intensity = 3.0;  
const int steps = 8;  
const float frequency = 4.0;  
const int angle = 7;
 
const float delta = 20.0;  
const float intence = 400.0;  
const float emboss = 0.3;  
 
float col(vec2 coord)  
{  
    float delta_theta = 2.0 * PI / float(angle);  
    float col = 0.0;  
    float theta = 0.0;  
    for (int i = 0; i < steps; i++)  
    {
        vec2 adjc = coord;
    theta = delta_theta * float(i);
    adjc.x += cos(theta)*time*speed + time * speed_x;  
        adjc.y -= sin(theta)*time*speed - time * speed_y;  
        col = col + cos((adjc.x*cos(theta) - adjc.y*sin(theta))  
            *frequency)*intensity;  
    }
    return cos(col);
}  

void main(void)  
{  
    vec2 p = (gl_FragCoord.xy) / resolution.xy, c1 = p, c2 = p;  
    float cc1 = col(c1);  
 
    c2.x += resolution.x/delta;  
    float dx = emboss*(cc1-col(c2))/delta;  
 
    c2.x = p.x;  
    c2.y += resolution.y/delta;  
    float dy = emboss*(cc1-col(c2))/delta;  
 
    c1.x += dx;  
    c1.y += dy;  
    float alpha = 1.+dot(dx,dy)*intence;  
    gl_FragColor = texture2D(tex0,c1)*(alpha) *v_color*(alpha);  
}

github地址为:
https://github.com/AlexandreRangel/QuaseCinemaFeijoada/blob/master/QuaseCinemaFeijoada07d/data/water.glsl

在使用顶点程序的时候,里面有个模型世界投影矩阵 u_MVPMatrix
我们在cocos2d-x中使用它的时候,需要把 CC_MVPMatrix 赋值给 u_MVPMatrix
或者直接把 u_MVPMatrix 删除,替换成 CC_MVPMatrix
like this

attribute vec4 a_color;  
attribute vec4 a_position;  
varying vec4 v_color;  

void main()  
{  
    v_color = a_color;  
    gl_Position = CC_MVPMatrix * a_position;  
}

cocos2d-x 在创建 GLProgram 的时候,会自动在 GLSL的文件之前加上 cocos2d-x 自身需要的参数

    const GLchar *sources[] = {
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
        (type == GL_VERTEX_SHADER ? "precision highp float;n" : "precision mediump float;n"),
#endif
        "uniform mat4 CC_PMatrix;n"
        "uniform mat4 CC_MVMatrix;n"
        "uniform mat4 CC_MVPMatrix;n"
        "uniform vec4 CC_Time;n"
        "uniform vec4 CC_SinTime;n"
        "uniform vec4 CC_CosTime;n"
        "uniform vec4 CC_Random01;n"
        "//CC INCLUDES ENDnn",
        source,
    };

that’s all.
本文对于原书教程补充的地方如下
修改 shader.vsh 替换 u_MVPMatrix 为 CC_MVPMatrix
删除 ShaderNode.cpp 中全部的 m_uniformCenter 的相关代码, m_uniformCenter 在Shader中并没有该字段,没有补完的意义
添加 ShaderNode.cpp 构造函数及SetColor方法
修改 ShaderNode.h 中 update 的参数

《移植:在Cocos2d-X中实现水波滤镜》有10个想法

  1. Cocos2d: OpenGL error 0x0502 in /Volumes/workspace/XCode_workspace/TestRipp/TestRipp/TestRipp/libs/cocos2dx/sprite_nodes/CCSprite.cpp draw 61

    1. @waffie 水波的原理是RTT,因此你在 addChild(水波,index) 的时候,其中的index 要高于你要产生的水波的物件的index. 即你的背景图addChild(bg,0),你的说水波addChild(wave,1) 必须高于bg的层级。

      1. 谢了,已解决,不过我感觉这个不实用,在电脑上运行帧率挺高的,在手机上只有几帧,而且还看不到效果。

  2. 我想询问一下 col 的计算是 什么 原理?
    float col(vec2 coord)
    这个部分…
    从形式上来看 应该是 类似于计算高度,
    但是这个高度 竟然 经过 一个 for 循环计算,
    不太理解 里面的 含义。

发表评论

电子邮件地址不会被公开。 必填项已用*标注