加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-14 18:53:17 所属栏目:百科 来源:网络整理
导读:背景 美术给出一套资源后,可以通过改变图片色相,复用同一套资源产生出多套资源的效果: 上图中蓝色是原始图片,利用代码改变图片色相后,可以产生效果差异明显的资源出来。像一些传统的游戏,如星际争霸等,都是通过这种技术实现了同一兵种,不同颜色种族

背景

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


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

实现理论原理

看上去非常神奇的转换,实际上是利用了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

普通代码实现

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

<span style="font-family:SimSun;font-size:14px;">Texture2D* HelloWorld::initTextureWithImage(Image *image,float _fhue)
{

	unsigned char*            tempData = NULL;
	bool                      hasAlpha = image->hasAlpha();
	Size                      imageSize = Size((float)(image->getWidth()),(float)(image->getHeight()));
	Texture2D::PixelFormat    pixelFormat;

	unsigned int width = image->getWidth();
	unsigned int height = image->getHeight();

	// Repack the pixel data into the right format
	unsigned int length = width * height;
	unsigned int newDataLen = 0;

	// Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBBB"
	float _f = _fhue / 60; //节省运算
	if (hasAlpha)
	{
		// compute pixel format
		pixelFormat = Texture2D::PixelFormat::RGBA8888;
		tempData = new unsigned char[length * 4];
		newDataLen = length * 4;
		unsigned char *outPixel8 = tempData;
		unsigned int* inPixel32 = (unsigned int*)image->getData();
		for (unsigned int i = 0; i < length; ++i,++inPixel32)
		{
			unsigned char* _colRGB = outPixel8;
			*outPixel8++ = (*inPixel32 >> 0) & 0xFF; // R
			*outPixel8++ = (*inPixel32 >> 8) & 0xFF; // G
			*outPixel8++ = (*inPixel32 >> 16) & 0xFF; // B
			*outPixel8 = (*inPixel32 >> 24) & 0xFF; // A
			//透明图层不做处理
			if (*outPixel8++)
			{
				unsigned char _r = *_colRGB;
				unsigned char _g = *(_colRGB + 1);
				unsigned char _b = *(_colRGB + 2);
				unsigned char min = (_r < _g) ? _r : _g;
				min = (min < _b) ? min : _b;

				unsigned char max = (_r > _g) ? _r : _g;
				max = (max > _b) ? max : _b;

				unsigned char temp = (max - min);
				float hsbH = 0; //temp

				if (temp)
				{
					if (max == _r) {
						if (_g >= _b)
						{
							hsbH = (float)(_g - _b) / (float)temp;
						}
						else
						{
							hsbH = ((float)(_g - _b) / (float)temp) + 6;
						}
					}
					else if (max == _g) {
						hsbH = ((float)(_b - _r) / (float)temp) + 2;
					}
					else if (max == _b) {
						hsbH = ((float)(_r - _g) / (float)temp) + 4;
					}
				}
				else
				{
					hsbH = 0;
				}

				hsbH += _f;
				if (hsbH < 0)
					hsbH += 6;
				else if (hsbH > 6)
					hsbH -= 6;

				char i = (int)hsbH;
				hsbH = hsbH - i;
				switch (i) {
				case 6:
				case 0:
					(*_colRGB++) = max;
					(*_colRGB++) = min + (int)(hsbH*temp);
					(*_colRGB++) = min;
					break;
				case 1:
					(*_colRGB++) = max - (int)(hsbH*temp);
					(*_colRGB++) = max;
					(*_colRGB++) = min;
					break;
				case 2:
					(*_colRGB++) = min;
					(*_colRGB++) = max;
					(*_colRGB++) = min + (int)(hsbH*temp);
					break;
				case 3:
					(*_colRGB++) = min;
					(*_colRGB++) = max - (int)(hsbH*temp);
					(*_colRGB++) = max;
					break;
				case 4:
					(*_colRGB++) = min + (int)(hsbH*temp);
					(*_colRGB++) = min;
					(*_colRGB++) = max;
					break;
				case 5:
					(*_colRGB++) = max;
					(*_colRGB++) = min;
					(*_colRGB++) = max - (int)(hsbH*temp);
					break;
				default:
					break;
				}
			}
		}
	}
	else
	{
		pixelFormat = Texture2D::PixelFormat::RGB888;
		tempData = new unsigned char[length * 3];
		newDataLen = length * 3;
		unsigned char *out3 = image->getData();
		unsigned char *outPixel8 = tempData;
		for (unsigned int i = 0; i < length; ++i)
		{
			unsigned char _r = *out3++;
			unsigned char _g = *out3++;
			unsigned char _b = *out3++;
			//changeHSLForRgb(255,0);
			// 			unsigned char *_nowRGB = changeHSLForRgb(_r,_g,_b,_fhue);
			// 			_r = *_nowRGB++;
			// 			_g = *_nowRGB++;
			// 			_b = *_nowRGB++;
			*outPixel8++ = _r; // R
			*outPixel8++ = _g; // G
			*outPixel8++ = _b; // B
		}
	}

	Texture2D* _text2d = new Texture2D();
	_text2d->initWithData(tempData,newDataLen,pixelFormat,width,height,imageSize);


	delete[] tempData;
	//_text2d->_hasPremultipliedAlpha = image->hasPremultipliedAlpha();
	return _text2d;
}</span>

利用Shader实现

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

colorHSL.fsh

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 v_texCoord;
uniform sampler2D CC_Texture0;
uniform float u_dH;
uniform float u_dS;
uniform float u_dL;

void main() {

    vec4 texColor=texture2D(CC_Texture0,v_texCoord);
    float r=texColor.r;
    float g=texColor.g;
    float b=texColor.b;
    float a=texColor.a;
    //convert rgb to hsl
    float h;
    float s;
    float l;
    {
        float max=max(max(r,g),b);
        float min=min(min(r,b);
        //----h
        if(max==min){

            h=0.0;
        }else if(max==r&&g>=b){
            h=60.0*(g-b)/(max-min)+0.0;
        }else if(max==r&&g<b){
            h=60.0*(g-b)/(max-min)+360.0;
        }else if(max==g){
            h=60.0*(b-r)/(max-min)+120.0;
        }else if(max==b){
            h=60.0*(r-g)/(max-min)+240.0;
        }
        //----l
        l=0.5*(max+min);
        //----s
        if(l==0.0||max==min){
            s=0.0;
        }else if(0.0<=l&&l<=0.5){
            s=(max-min)/(2.0*l);
        }else if(l>0.5){
            s=(max-min)/(2.0-2.0*l);
        }
    }
    //(h,s,l)+(dH,dS,dL) -> (h,l)
    h=h+u_dH;
    s=min(1.0,max(0.0,s+u_dS));
    l=l+u_dL;
    //convert (h,l) to rgb and got final color
    vec4 finalColor;
    {
        float q;
        if(l<0.5){
            q=l*(1.0+s);
        }else if(l>=0.5){
            q=l+s-l*s;
        }
        float p=2.0*l-q;
        float hk=h/360.0;
        float t[3];
        t[0]=hk+1.0/3.0;t[1]=hk;t[2]=hk-1.0/3.0;
        for(int i=0;i<3;i++){
            if(t[i]<0.0)t[i]+=1.0;
            if(t[i]>1.0)t[i]-=1.0;
        }//got t[i]
        float c[3];
        for(int i=0;i<3;i++){
            if(t[i]<1.0/6.0){
                c[i]=p+((q-p)*6.0*t[i]);
            }else if(1.0/6.0<=t[i]&&t[i]<0.5){
                c[i]=q;
            }else if(0.5<=t[i]&&t[i]<2.0/3.0){
                c[i]=p+((q-p)*6.0*(2.0/3.0-t[i]));
            }else{
                c[i]=p;
            }
        }
        finalColor=vec4(c[0],c[1],c[2],a);
    }

    finalColor+=vec4(u_dL,u_dL,0.0);

    gl_FragColor=finalColor;

}

以下适用COCOS2.2版本

.H中增加以下代码

void setHSLMode();
void setHSL(float h,float s,float l);
void updateHSL();

float m_dH;
float m_dS;
float m_dL;

GLuint m_dHlocation;
GLuint m_dSlocation;
GLuint m_dLlocation;


具体实现

void GameColorSprite::setHSLMode(){

    ccBlendFunc blendFunc={GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA};
    this->setBlendFunc(blendFunc);

    GLchar * fragSource = (GLchar*) CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename("colorHSL.fsh").c_str())->getCString();

    CGLProgramWithUnifos* pProgram = new CGLProgramWithUnifos();
    pProgram->initWithVertexShaderByteArray(ccPositionTextureColor_vert,fragSource);
    this->setShaderProgram(pProgram);
    pProgram->release();

    CHECK_GL_ERROR_DEBUG();

    this->getShaderProgram()->addAttribute(kCCAttributeNamePosition,kCCVertexAttrib_Position);
    this->getShaderProgram()->addAttribute(kCCAttributeNameColor,kCCVertexAttrib_Color);
    this->getShaderProgram()->addAttribute(kCCAttributeNameTexCoord,kCCVertexAttrib_TexCoords);
    CHECK_GL_ERROR_DEBUG();

    this->getShaderProgram()->link();
    CHECK_GL_ERROR_DEBUG();

    this->getShaderProgram()->updateUniforms();
    CHECK_GL_ERROR_DEBUG();

    m_dHlocation = glGetUniformLocation(getShaderProgram()->getProgram(),"u_dH");
    m_dSlocation = glGetUniformLocation(getShaderProgram()->getProgram(),"u_dS");
    m_dLlocation = glGetUniformLocation(getShaderProgram()->getProgram(),"u_dL");

    updateHSL();
}

void GameColorSprite::setHSL(float h,float l){
    m_dH = h;
    m_dS = s;
    m_dL = l;
    updateHSL();
}

void GameColorSprite::updateHSL(){
    glUniform1f(m_dHlocation,m_dH);
    glUniform1f(m_dSlocation,m_dS);
    glUniform1f(m_dLlocation,m_dL);
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读