在 cocos2d-x 3.x中使用shader实现精灵色相(Hue)的修改

为cocos2d 的精灵修改色相(Hue)的问题困扰了我很久,网上一直没有找到比较靠谱的解决方案。
前段时间在https://github.com/alex314/CCSpriteWithHue发现国外开发者在 cocos2d-iPhone 2.x版本上面实现了对精灵的色相修改,最近有时间在cocos2d-x 3.x 上重新实现了一下。

实现的效果如下:

原图:


色相修改:




关键代码如下:

CCSpriteWithHue.h

//
//  CCSpriteWithHue.h
//  SnippetsProject
//
//  Created by Lawis on 15/1/20.
//
//

#ifndef __SnippetsProject__CCSpriteWithHue__
#define __SnippetsProject__CCSpriteWithHue__

#include "cocos2d.h"
USING_NS_CC;

class CCSpriteWithHue :public Sprite {
    
private://variable
    
    GLint m_hueLocation;
    GLint m_alphaLocation;
    
protected://variable
    


public://variable
    
    CC_SYNTHESIZE_READONLY(GLfloat, m_hue, Hue);
    
private://method
    
    
  
protected://method
    
    virtual void setupDefaultSettings();
    
    virtual void getUniformLocations();
    
    virtual void updateColorMatrix();
    
    virtual void updateAlpha();
    
    virtual GLfloat alpha();
    
    virtual void updateColor();
    
    virtual bool initWithTexture(Texture2D *texture, const Rect& rect, bool rotated);
    
    virtual void initShader();
    
public://method
    
    CCSpriteWithHue();
    
    virtual ~CCSpriteWithHue();
    
    static CCSpriteWithHue* create(const std::string& filename);
    
    virtual void setHue(GLfloat _hue);
    
    
    
};


#endif /* defined(__SnippetsProject__CCSpriteWithHue__) */


CCSpriteWithHue.cpp

//
//  CCSpriteWithHue.cpp
//  SnippetsProject
//
//  Created by Lawis on 15/1/20.
//
//

#include "CCSpriteWithHue.h"


void xRotateMat(float mat[3][3], float rs, float rc);
void yRotateMat(float mat[3][3], float rs, float rc);
void zRotateMat(float mat[3][3], float rs, float rc);
void matrixMult(float a[3][3], float b[3][3], float c[3][3]);
void hueMatrix(GLfloat mat[3][3], float angle);
void premultiplyAlpha(GLfloat mat[3][3], float alpha);

CCSpriteWithHue::CCSpriteWithHue()
{
    
}

CCSpriteWithHue::~CCSpriteWithHue()
{
    
}

CCSpriteWithHue* CCSpriteWithHue::create(const std::string& filename)
{
    CCSpriteWithHue *sprite = new (std::nothrow) CCSpriteWithHue();
    if (sprite && sprite->initWithFile(filename))
    {
        sprite->autorelease();
        return sprite;
    }
    CC_SAFE_DELETE(sprite);
    return nullptr;
}

bool CCSpriteWithHue::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated)
{
    
    if (!Sprite::initWithTexture(texture, rect, rotated)) {
        return false;
    }
    this->setupDefaultSettings();
    this->initShader();
    return true;
}

void CCSpriteWithHue::setupDefaultSettings()
{
    this->m_hue = 0.0;
}

void CCSpriteWithHue::initShader()
{
    GLProgram * p = new GLProgram();
    this->setGLProgram(p);
    p->release();
    p->initWithFilenames("hueChange.vsh", "hueChange.fsh");
    p->link();
    p->updateUniforms();
    this->getUniformLocations();
    this->updateColor();
}

void CCSpriteWithHue::getUniformLocations()
{
    m_hueLocation = glGetUniformLocation(this->getGLProgram()->getProgram(), "u_hue");
    m_alphaLocation = glGetUniformLocation(this->getGLProgram()->getProgram(), "u_alpha");
}

void CCSpriteWithHue::updateColorMatrix()
{
    this->getGLProgram()->use();
    GLfloat mat[3][3];
    memset(mat, 0, sizeof(GLfloat)*9);
    hueMatrix(mat, m_hue);
    premultiplyAlpha(mat, this->alpha());
    glUniformMatrix3fv(m_hueLocation, 1, GL_FALSE, (GLfloat *)&mat);
}

void CCSpriteWithHue::updateAlpha()
{
    this->getGLProgram()->use();
    glUniform1f(m_alphaLocation, this->alpha());
}

GLfloat CCSpriteWithHue::alpha()
{
    
    return _displayedOpacity / 255.0f;
}

void CCSpriteWithHue::setHue(GLfloat _hue)
{
    m_hue = _hue;
    this->updateColorMatrix();
}

void CCSpriteWithHue::updateColor()
{
    Sprite::updateColor();
    this->updateColorMatrix();
    this->updateAlpha();
}


#pragma mark -

void xRotateMat(float mat[3][3], float rs, float rc)
{
    mat[0][0] = 1.0;
    mat[0][1] = 0.0;
    mat[0][2] = 0.0;
    
    mat[1][0] = 0.0;
    mat[1][1] = rc;
    mat[1][2] = rs;
    
    mat[2][0] = 0.0;
    mat[2][1] = -rs;
    mat[2][2] = rc;
}

void yRotateMat(float mat[3][3], float rs, float rc)
{
    mat[0][0] = rc;
    mat[0][1] = 0.0;
    mat[0][2] = -rs;
    
    mat[1][0] = 0.0;
    mat[1][1] = 1.0;
    mat[1][2] = 0.0;
    
    mat[2][0] = rs;
    mat[2][1] = 0.0;
    mat[2][2] = rc;
}


void zRotateMat(float mat[3][3], float rs, float rc)
{
    mat[0][0] = rc;
    mat[0][1] = rs;
    mat[0][2] = 0.0;
    
    mat[1][0] = -rs;
    mat[1][1] = rc;
    mat[1][2] = 0.0;
    
    mat[2][0] = 0.0;
    mat[2][1] = 0.0;
    mat[2][2] = 1.0;
}

void matrixMult(float a[3][3], float b[3][3], float c[3][3])
{
    int x, y;
    float temp[3][3];
    
    for(y=0; y<3; y++) {
        for(x=0; x<3; x++) {
            temp[y][x] = b[y][0] * a[0][x] + b[y][1] * a[1][x] + b[y][2] * a[2][x];
        }
    }
    for(y=0; y<3; y++) {
        for(x=0; x<3; x++) {
            c[y][x] = temp[y][x];
        }
    }
}

void hueMatrix(GLfloat mat[3][3], float angle)
{
#define SQRT_2      sqrt(2.0)
#define SQRT_3      sqrt(3.0)
    
    float mag, rot[3][3];
    float xrs, xrc;
    float yrs, yrc;
    float zrs, zrc;
    
    // Rotate the grey vector into positive Z
    mag = SQRT_2;
    xrs = 1.0/mag;
    xrc = 1.0/mag;
    xRotateMat(mat, xrs, xrc);
    mag = SQRT_3;
    yrs = -1.0/mag;
    yrc = SQRT_2/mag;
    yRotateMat(rot, yrs, yrc);
    matrixMult(rot, mat, mat);
    
    // Rotate the hue
    zrs = sin(angle);
    zrc = cos(angle);
    zRotateMat(rot, zrs, zrc);
    matrixMult(rot, mat, mat);
    
    // Rotate the grey vector back into place
    yRotateMat(rot, -yrs, yrc);
    matrixMult(rot,  mat, mat);
    xRotateMat(rot, -xrs, xrc);
    matrixMult(rot,  mat, mat);
}

void premultiplyAlpha(GLfloat mat[3][3], float alpha)
{
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            mat[i][j] *= alpha;
        }
    }
}

hueChange.fsh

varying vec2 v_texCoord;
uniform mat3 u_hue;
uniform float u_alpha;

void main()
{
    vec4 pixColor = texture2D(CC_Texture0, v_texCoord);
    vec3 rgbColor ;
    rgbColor = u_hue * pixColor.rgb;
    
    gl_FragColor = vec4(rgbColor.r,rgbColor.g,rgbColor.b, pixColor.a * u_alpha);
}


hueChange.vsh

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

varying vec2 v_texCoord;

void main()
{
    gl_Position = CC_PMatrix * a_position;
    v_texCoord = a_texCoord;
}

注意 :Debug模式下,需要注释掉 CCGLProgramState.cpp  apply() 方法中 switch语句default分支下的断言


调用方法如下:

CSpriteWithHue *sprite = CCSpriteWithHue::create("HelloWorld.png");
sprite->setHue(4.6);//值在 0 ~ 2 Pi 之间




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值