Cocos2D-X shader(四) 利用shader改变图片色相(Hue)

背景

美术给出一套资源后,可以通过改变图片色相,复用同一套资源产生出多套资源的效果:


上图中蓝色是原始图片,利用代码改变图片色相后,可以产生效果差异明显的资源出来。像一些传统的游戏,如星际争霸等,都是通过这种技术实现了同一兵种,不同颜色种族的特效。

实现理论原理

看上去非常神奇的转换,实际上是利用了HSV格式图像处理的技术:

传统RGB模型:RGB是一种加色模式 将不同比例的RED/GREEN/BLUE混合在一起得到新的颜色 
这里写图片描述

HSV(HSB)模型:通过色相/饱和度/亮度来得到颜色 
H(hue):色相 表示颜色的类型 值域[0,360] 
S(Saturation):饱和度 从灰度到纯色 值域[0,1] 
V(Value or Brightness):亮度 从黑色到特定饱和度的颜色 值域[0,1]

HSV模型图 
这里写图片描述

RGB到HSV的转换公式 
这里写图片描述

HSV到RGB的转换公式 
这里写图片描述

公式可以参考 
http://baike.baidu.com/subview/541362/8445478.htm?fr=aladdin

普通代码实现

利用上述转换公式,实现代码如下(透明像素不处理):

  1. <span style="font-family:SimSun;font-size:14px;">Texture2D* HelloWorld::initTextureWithImage(Image *image, float _fhue)  
  2. {  
  3.   
  4.     unsigned char*            tempData = NULL;  
  5.     bool                      hasAlpha = image->hasAlpha();  
  6.     Size                      imageSize = Size((float)(image->getWidth()), (float)(image->getHeight()));  
  7.     Texture2D::PixelFormat    pixelFormat;  
  8.   
  9.     unsigned int width = image->getWidth();  
  10.     unsigned int height = image->getHeight();  
  11.   
  12.     // Repack the pixel data into the right format  
  13.     unsigned int length = width * height;  
  14.     unsigned int newDataLen = 0;  
  15.   
  16.     // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBBB"  
  17.     float _f = _fhue / 60; //节省运算  
  18.     if (hasAlpha)  
  19.     {  
  20.         // compute pixel format  
  21.         pixelFormat = Texture2D::PixelFormat::RGBA8888;  
  22.         tempData = new unsigned char[length * 4];  
  23.         newDataLen = length * 4;  
  24.         unsigned char *outPixel8 = tempData;  
  25.         unsigned int* inPixel32 = (unsigned int*)image->getData();  
  26.         for (unsigned int i = 0; i < length; ++i, ++inPixel32)  
  27.         {  
  28.             unsigned char* _colRGB = outPixel8;  
  29.             *outPixel8++ = (*inPixel32 >> 0) & 0xFF; // R  
  30.             *outPixel8++ = (*inPixel32 >> 8) & 0xFF; // G  
  31.             *outPixel8++ = (*inPixel32 >> 16) & 0xFF; // B  
  32.             *outPixel8 = (*inPixel32 >> 24) & 0xFF; // A  
  33.             //透明图层不做处理  
  34.             if (*outPixel8++)  
  35.             {  
  36.                 unsigned char _r = *_colRGB;  
  37.                 unsigned char _g = *(_colRGB + 1);  
  38.                 unsigned char _b = *(_colRGB + 2);  
  39.                 unsigned char min = (_r < _g) ? _r : _g;  
  40.                 min = (min < _b) ? min : _b;  
  41.   
  42.                 unsigned char max = (_r > _g) ? _r : _g;  
  43.                 max = (max > _b) ? max : _b;  
  44.   
  45.                 unsigned char temp = (max - min);  
  46.                 float hsbH = 0; //temp  
  47.   
  48.                 if (temp)  
  49.                 {  
  50.                     if (max == _r) {  
  51.                         if (_g >= _b)  
  52.                         {  
  53.                             hsbH = (float)(_g - _b) / (float)temp;  
  54.                         }  
  55.                         else  
  56.                         {  
  57.                             hsbH = ((float)(_g - _b) / (float)temp) + 6;  
  58.                         }  
  59.                     }  
  60.                     else if (max == _g) {  
  61.                         hsbH = ((float)(_b - _r) / (float)temp) + 2;  
  62.                     }  
  63.                     else if (max == _b) {  
  64.                         hsbH = ((float)(_r - _g) / (float)temp) + 4;  
  65.                     }  
  66.                 }  
  67.                 else  
  68.                 {  
  69.                     hsbH = 0;  
  70.                 }  
  71.   
  72.                 hsbH += _f;  
  73.                 if (hsbH < 0)  
  74.                     hsbH += 6;  
  75.                 else if (hsbH > 6)  
  76.                     hsbH -= 6;  
  77.   
  78.                 char i = (int)hsbH;  
  79.                 hsbH = hsbH - i;  
  80.                 switch (i) {  
  81.                 case 6:  
  82.                 case 0:  
  83.                     (*_colRGB++) = max;  
  84.                     (*_colRGB++) = min + (int)(hsbH*temp);  
  85.                     (*_colRGB++) = min;  
  86.                     break;  
  87.                 case 1:  
  88.                     (*_colRGB++) = max - (int)(hsbH*temp);  
  89.                     (*_colRGB++) = max;  
  90.                     (*_colRGB++) = min;  
  91.                     break;  
  92.                 case 2:  
  93.                     (*_colRGB++) = min;  
  94.                     (*_colRGB++) = max;  
  95.                     (*_colRGB++) = min + (int)(hsbH*temp);  
  96.                     break;  
  97.                 case 3:  
  98.                     (*_colRGB++) = min;  
  99.                     (*_colRGB++) = max - (int)(hsbH*temp);  
  100.                     (*_colRGB++) = max;  
  101.                     break;  
  102.                 case 4:  
  103.                     (*_colRGB++) = min + (int)(hsbH*temp);  
  104.                     (*_colRGB++) = min;  
  105.                     (*_colRGB++) = max;  
  106.                     break;  
  107.                 case 5:  
  108.                     (*_colRGB++) = max;  
  109.                     (*_colRGB++) = min;  
  110.                     (*_colRGB++) = max - (int)(hsbH*temp);  
  111.                     break;  
  112.                 default:  
  113.                     break;  
  114.                 }  
  115.             }  
  116.         }  
  117.     }  
  118.     else  
  119.     {  
  120.         pixelFormat = Texture2D::PixelFormat::RGB888;  
  121.         tempData = new unsigned char[length * 3];  
  122.         newDataLen = length * 3;  
  123.         unsigned char *out3 = image->getData();  
  124.         unsigned char *outPixel8 = tempData;  
  125.         for (unsigned int i = 0; i < length; ++i)  
  126.         {  
  127.             unsigned char _r = *out3++;  
  128.             unsigned char _g = *out3++;  
  129.             unsigned char _b = *out3++;  
  130.             //changeHSLForRgb(255, 0, 0);  
  131.             //          unsigned char *_nowRGB = changeHSLForRgb(_r,_g,_b,_fhue);  
  132.             //          _r = *_nowRGB++;  
  133.             //          _g = *_nowRGB++;  
  134.             //          _b = *_nowRGB++;  
  135.             *outPixel8++ = _r; // R  
  136.             *outPixel8++ = _g; // G  
  137.             *outPixel8++ = _b; // B  
  138.         }  
  139.     }  
  140.   
  141.     Texture2D* _text2d = new Texture2D();  
  142.     _text2d->initWithData(tempData, newDataLen, pixelFormat, width, height, imageSize);  
  143.   
  144.   
  145.     delete[] tempData;  
  146.     //_text2d->_hasPremultipliedAlpha = image->hasPremultipliedAlpha();  
  147.     return _text2d;  
  148. }</span>  

利用Shader实现

Shader可以利用GPU提升渲染效率:

colorHSL.fsh

  1. #ifdef GL_ES  
  2. precision mediump float;  
  3. #endif  
  4.   
  5. varying vec2 v_texCoord;  
  6. uniform sampler2D CC_Texture0;  
  7. uniform float u_dH;  
  8. uniform float u_dS;  
  9. uniform float u_dL;  
  10.   
  11. void main() {  
  12.   
  13.     vec4 texColor=texture2D(CC_Texture0, v_texCoord);  
  14.     float r=texColor.r;  
  15.     float g=texColor.g;  
  16.     float b=texColor.b;  
  17.     float a=texColor.a;  
  18.     //convert rgb to hsl  
  19.     float h;  
  20.     float s;  
  21.     float l;  
  22.     {  
  23.         float max=max(max(r,g),b);  
  24.         float min=min(min(r,g),b);  
  25.         //----h  
  26.         if(max==min){  
  27.   
  28.             h=0.0;  
  29.         }else if(max==r&&g>=b){  
  30.             h=60.0*(g-b)/(max-min)+0.0;  
  31.         }else if(max==r&&g<b){  
  32.             h=60.0*(g-b)/(max-min)+360.0;  
  33.         }else if(max==g){  
  34.             h=60.0*(b-r)/(max-min)+120.0;  
  35.         }else if(max==b){  
  36.             h=60.0*(r-g)/(max-min)+240.0;  
  37.         }  
  38.         //----l  
  39.         l=0.5*(max+min);  
  40.         //----s  
  41.         if(l==0.0||max==min){  
  42.             s=0.0;  
  43.         }else if(0.0<=l&&l<=0.5){  
  44.             s=(max-min)/(2.0*l);  
  45.         }else if(l>0.5){  
  46.             s=(max-min)/(2.0-2.0*l);  
  47.         }  
  48.     }  
  49.     //(h,s,l)+(dH,dS,dL) -> (h,s,l)  
  50.     h=h+u_dH;  
  51.     s=min(1.0,max(0.0,s+u_dS));  
  52.     l=l+u_dL;  
  53.     //convert (h,s,l) to rgb and got final color  
  54.     vec4 finalColor;  
  55.     {  
  56.         float q;  
  57.         if(l<0.5){  
  58.             q=l*(1.0+s);  
  59.         }else if(l>=0.5){  
  60.             q=l+s-l*s;  
  61.         }  
  62.         float p=2.0*l-q;  
  63.         float hk=h/360.0;  
  64.         float t[3];  
  65.         t[0]=hk+1.0/3.0;t[1]=hk;t[2]=hk-1.0/3.0;  
  66.         for(int i=0;i<3;i++){  
  67.             if(t[i]<0.0)t[i]+=1.0;  
  68.             if(t[i]>1.0)t[i]-=1.0;  
  69.         }//got t[i]  
  70.         float c[3];  
  71.         for(int i=0;i<3;i++){  
  72.             if(t[i]<1.0/6.0){  
  73.                 c[i]=p+((q-p)*6.0*t[i]);  
  74.             }else if(1.0/6.0<=t[i]&&t[i]<0.5){  
  75.                 c[i]=q;  
  76.             }else if(0.5<=t[i]&&t[i]<2.0/3.0){  
  77.                 c[i]=p+((q-p)*6.0*(2.0/3.0-t[i]));  
  78.             }else{  
  79.                 c[i]=p;  
  80.             }  
  81.         }  
  82.         finalColor=vec4(c[0],c[1],c[2],a);  
  83.     }  
  84.   
  85.     finalColor+=vec4(u_dL,u_dL,u_dL,0.0);  
  86.   
  87.     gl_FragColor=finalColor;  
  88.   
  89. }  

以下适用COCOS2.2版本

.H中增加以下代码

  1. void setHSLMode();  
  2. void setHSL(float h , float s, float l);  
  3. void updateHSL();  
  4.   
  5. float m_dH;  
  6. float m_dS;  
  7. float m_dL;  
  8.   
  9. GLuint m_dHlocation;  
  10. GLuint m_dSlocation;  
  11. GLuint m_dLlocation;  


具体实现

  1. void GameColorSprite::setHSLMode(){  
  2.   
  3.     ccBlendFunc blendFunc={GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA};  
  4.     this->setBlendFunc(blendFunc);  
  5.   
  6.     GLchar * fragSource = (GLchar*) CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename("colorHSL.fsh").c_str())->getCString();  
  7.   
  8.     CGLProgramWithUnifos* pProgram = new CGLProgramWithUnifos();  
  9.     pProgram->initWithVertexShaderByteArray(ccPositionTextureColor_vert, fragSource);  
  10.     this->setShaderProgram(pProgram);  
  11.     pProgram->release();  
  12.   
  13.     CHECK_GL_ERROR_DEBUG();  
  14.   
  15.     this->getShaderProgram()->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);  
  16.     this->getShaderProgram()->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);  
  17.     this->getShaderProgram()->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);  
  18.     CHECK_GL_ERROR_DEBUG();  
  19.   
  20.     this->getShaderProgram()->link();  
  21.     CHECK_GL_ERROR_DEBUG();  
  22.   
  23.     this->getShaderProgram()->updateUniforms();  
  24.     CHECK_GL_ERROR_DEBUG();  
  25.   
  26.     m_dHlocation = glGetUniformLocation(getShaderProgram()->getProgram(), "u_dH");  
  27.     m_dSlocation = glGetUniformLocation(getShaderProgram()->getProgram(), "u_dS");  
  28.     m_dLlocation = glGetUniformLocation(getShaderProgram()->getProgram(), "u_dL");  
  29.   
  30.     updateHSL();  
  31. }  

  1. void GameColorSprite::setHSL(float h , float s, float l){  
  2.     m_dH = h;  
  3.     m_dS = s;  
  4.     m_dL = l;  
  5.     updateHSL();  
  6. }  

  1. void GameColorSprite::updateHSL(){  
  2.     glUniform1f(m_dHlocation,m_dH);  
  3.     glUniform1f(m_dSlocation,m_dS);  
  4.     glUniform1f(m_dLlocation,m_dL);  
  5. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VCHH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值