移植:在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 的参数

发表评论

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