cocos2dx spine之二 :spine变色

cocos2dx版本为3.10

1.具体原理和代码可以参考博文《利用shader改变图片色相Hue》,下面的代码根据该博文进行整理优化。

基本原理就是将RGB值转换为HSL值后加上输入的HSL值,再转换为RGB值。

2.spine变色的思路有三种:

①spine::SkeletonAnimation调用shader

②读取spine对应的atlas文件,分析该文件得到所需的png图片,将该图片读入内存,修改内存中像素颜色,然后生成texture赋值给spine中的spAtlas->pages->rendererObject

③读取spine对应的atlas文件,分析该文件得到所需的png图片,将该图片读入内存,修改内存中像素颜色,保存为新的png图片;读取atlas文件,修改里面png名字为新png图片名字,保存为新的atlas文件;spine创建时使用新的png图片和新的atlas文件;

以上三种方法都可行,但在实现过程中我选择使用了第③种方法;

第①种方法简单易用,但是打包到一些低端的android手机时发现掉帧很厉害,所以才会有第②和第③种方法。

第②种方法经过测试也是可行的,但是破坏了spine的正常创建流程,导致对源码的修改过于复杂容易导致各种问题。

第③种方法独立在正常创建spine流程之前,更稳当可控。

3.根据资料色相H值的范围在[0,360],饱和度S值的范围在[0,1],明度L值的范围在[0,1];在PhotoShop中(快捷键ctrl+u)可调值范围为色相H[-180,180],饱和度A[-100,100],明度I[-100,100];

所以在代码中,H值不进行转换,S值和L值都除以100;在实际测试过程中发现,PS调试出来的效果和程序得到的效果在S/L不为0的情况下还是有区别的,具体原因不懂,估计可能是算法不一致吧。

4.第①种方法shader

①shader代码

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

②调用代码

1     auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_vert, shader_content);
2     GLProgramState* glState = GLProgramState::getOrCreateWithGLProgram(glprogram);
3
4     glState->setUniformFloat("u_dH", h);
5     glState->setUniformFloat("u_dS", s / 100);
6     glState->setUniformFloat("u_dL", l / 100);
7
8     skeleton_animation->setGLProgramState(glState);

5.第③种方法创建新的png文件和atlas文件

①创建新png文件函数

  1 Image* create_new_image_hsl(Image* image, float u_dH, float u_dS, float u_dL){
  2     u_dH = u_dH;
  3     u_dS = u_dS / 100.0f;
  4     u_dL = u_dL / 100.0f;
  5
  6     bool hasAlpha = image->hasAlpha();
  7     Texture2D::PixelFormat pixelFormat = image->getRenderFormat();
  8     int bit_per_pixel = image->getBitPerPixel(); //每像素多少位
  9     unsigned int length = image->getWidth()*image->getHeight();
 10
 11     if (hasAlpha){
 12         //只处理了RGBA8888的格式
 13         if (pixelFormat == Texture2D::PixelFormat::RGBA8888){
 14             unsigned int* inPixel32 = (unsigned int*)image->getData();
 15             for (unsigned int i = 0; i < length; ++i, ++inPixel32)
 16             {
 17                 unsigned char cr = (*inPixel32 >> 0) & 0xFF; // R
 18                 unsigned char cg = (*inPixel32 >> 8) & 0xFF; // G
 19                 unsigned char cb = (*inPixel32 >> 16) & 0xFF; // B
 20                 unsigned char ca = (*inPixel32 >> 24) & 0xFF; // A
 21                 //透明图层不做处理
 22                 if (ca){
 23                     float r = cr*1.0f / 0xFF;
 24                     float g = cg*1.0f / 0xFF;
 25                     float b = cb*1.0f / 0xFF;
 26
 27                     float h = 0;
 28                     float s = 0;
 29                     float l = 0;
 30                     {
 31                         float max = r > g ? r : g;
 32                         max = max > b ? max : b;
 33
 34                         float min = r < g ? r : g;
 35                         min = min < b ? min : b;
 36
 37                         //----h
 38                         if (max == min){
 39                             h = 0;
 40                         }
 41                         else if (max == r && g >= b){
 42                             h = 60.0f*(g - b) / (max - min);
 43                         }
 44                         else if (max == r && g < b){
 45                             h = 60.0f*(g - b) / (max - min) + 360.0f;
 46                         }
 47                         else if (max == g){
 48                             h = 60.0f*(b - r) / (max - min) + 120.0f;
 49                         }
 50                         else if (max == b){
 51                             h = 60.0f*(r - g) / (max - min) + 240.0f;
 52                         }
 53                         //----l
 54                         l = 0.5f*(max + min);
 55                         //----s
 56                         if (l == 0 || max == min){
 57                             s = 0;
 58                         }
 59                         else if (0 <= l&&l <= 0.5f){
 60                             s = (max - min) / (2.0f*l);
 61                         }
 62                         else if (l > 0.5f){
 63                             s = (max - min) / (2.0f - 2.0f*l);
 64                         }
 65                     }
 66                     //(h,s,l)+(dH,dS,dL) -> (h,s,l)
 67                     h = h + u_dH;
 68                     s = 0 > s + u_dS ? 0 : s + u_dS;
 69                     s = s < 1.0f ? s : 1.0f;
 70
 71                     l = l + u_dL;
 72                     //convert (h,s,l) to rgb and got final color
 73
 74                     //vec4 finalColor;
 75                     {
 76                         float q;
 77                         if (l < 0.5f){
 78                             q = l*(1.0f + s);
 79                         }
 80                         else if (l >= 0.5f){
 81                             q = l + s - l*s;
 82                         }
 83                         float p = 2.0f*l - q;
 84                         float hk = h / 360.0f;
 85
 86                         float t[3] = {};
 87                         t[0] = hk + 1.0f / 3.0f;
 88                         t[1] = hk;
 89                         t[2] = hk - 1.0f / 3.0f;
 90
 91                         float c[3] = {};
 92                         for (int i = 0; i < 3; i++){
 93                             if (t[i] < 0){
 94                                 t[i] += 1.0f;
 95                             }
 96                             if (t[i] > 1.0f){
 97                                 t[i] -= 1.0f;
 98                             }
 99
100                             if (t[i] < 1.0f / 6.0f){
101                                 c[i] = p + ((q - p)*6.0f*t[i]);
102                             }
103                             else if (1.0f / 6.0f <= t[i] && t[i] < 0.5f){
104                                 c[i] = q;
105                             }
106                             else if (0.5f <= t[i] && t[i] < 2.0f / 3.0f){
107                                 c[i] = p + ((q - p)*6.0f*(2.0f / 3.0f - t[i]));
108                             }
109                             else{
110                                 c[i] = p;
111                             }
112                         }
113                         //finalColor = vec4(c[0], c[1], c[2], a);
114
115                         r = c[0];
116                         g = c[1];
117                         b = c[2];
118                     }
119
120                     //finalColor += vec4(u_dL, u_dL, u_dL, 0.0);
121                     r += u_dL;
122                     g += u_dL;
123                     b += u_dL;
124
125                     if (r > 1.0f) r = 1.0f;
126                     if (g > 1.0f) g = 1.0f;
127                     if (b > 1.0f) b = 1.0f;
128                     if (r < 0) r = 0;
129                     if (g < 0) g = 0;
130                     if (b < 0) b = 0;
131
132                     unsigned char final_r = r * 0xFF;
133                     unsigned char final_g = g * 0xFF;
134                     unsigned char final_b = b * 0xFF;
135                     unsigned char final_a = ca;
136
137
138                     unsigned int val = final_a;
139                     val = val << 8;
140                     val |= final_b;
141                     val = val << 8;
142                     val |= final_g;
143                     val = val << 8;
144                     val |= final_r;
145
146                     *inPixel32 = val;
147                 }
148             }
149         }
150     }
151
152     return image;
153 }

②分析atlas文件,得到对应的png文件,并生成新png文件和atlas文件

 1 void create_new_png_atlas_hls(const std::string& atlasFile, float u_dH, float u_dS, float u_dL)
 2 {
 3     std::string atlas_file_full_name = FileUtils::getInstance()->fullPathForFilename(atlasFile);
 4     Data data = FileUtils::getInstance()->getDataFromFile(atlas_file_full_name);
 5
 6     char* bytes = new char[data.getSize()]();
 7     memcpy(bytes, data.getBytes(), data.getSize());
 8
 9     //遍历得到第一个‘\n‘,则是png名字
10     int start_idx = 0, end_idx = 0;
11     for (int i = 0; i < data.getSize(); ++i){
12         end_idx = i;
13
14         if (bytes[i] == ‘\n‘){
15             //判断是否png
16             int len = end_idx - start_idx;
17             if (len > 4 && bytes[end_idx - 4] == ‘.‘ &&
18                 (bytes[end_idx - 3] == ‘p‘ || bytes[end_idx - 3] == ‘P‘) &&
19                 (bytes[end_idx - 2] == ‘n‘ || bytes[end_idx - 2] == ‘N‘) &&
20                 (bytes[end_idx - 1] == ‘g‘ || bytes[end_idx - 1] == ‘G‘)){
21
22                 //新文件路径
23                 char sz_atlas_path[256] = {};
24                 memcpy(sz_atlas_path, atlas_file_full_name.c_str(), atlas_file_full_name.size() - 6);
25                 std::string new_atlas_path = StringUtils::format("%s_%d_%d_%d.atlas", sz_atlas_path, (int)u_dH, (int)u_dS, (int)u_dL);
26
27                 //png的名字
28                 char png_name[256] = {};
29                 memcpy(png_name, bytes + start_idx, len - 4);
30                 std::string new_png_name = StringUtils::format("%s_%d_%d_%d.png", png_name, (int)u_dH, (int)u_dS, (int)u_dL);
31
32                 //根据新名字生成新的atlas文件
33                 unsigned char* new_atlas_content = new unsigned char[data.getSize() + new_png_name.size()]();
34                 int len = 0;
35                 memcpy(new_atlas_content, bytes, start_idx);
36                 len += start_idx;
37                 memcpy(new_atlas_content + len, new_png_name.c_str(), new_png_name.size());
38                 len += new_png_name.size();
39                 if (end_idx < data.getSize()){
40                     memcpy(new_atlas_content + len, bytes + end_idx, data.getSize() - end_idx);
41                 }
42                 len += (data.getSize() - end_idx);
43
44                 Data data;
45                 data.copy(new_atlas_content, len);
46
47                 if (!FileUtils::getInstance()->writeDataToFile(data, new_atlas_path)){
48                     cocos2d::log("save file %s error.", new_atlas_path.c_str());
49                 }
50
51                 data.clear();
52                 delete[]new_atlas_content;
53
54
55                 //写入新的图片
56                 std::string png_file_full_name = FileUtils::getInstance()->fullPathForFilename(StringUtils::format("%s.png", png_name));
57                 char sz_png_path[256] = {};
58                 memcpy(sz_png_path, png_file_full_name.c_str(), png_file_full_name.size() - 4);
59                 std::string  new_png_path = StringUtils::format("%s_%d_%d_%d.png", sz_png_path, (int)u_dH, (int)u_dS, (int)u_dL);
60
61                 Image* image = new Image();
62                 image->initWithImageFile(png_file_full_name);
63
64                 Image* new_image = create_new_image_hsl(image, u_dH, u_dS, u_dL);
65                 if (!new_image->saveToFile(new_png_path, false)){
66                     cocos2d::log("save file %s error.", new_png_path.c_str());
67                 }
68                 delete image;
69
70                 break;
71             }
72
73             start_idx = i + 1;
74         }
75     }
76     delete[]bytes;
77 }
时间: 2024-08-11 08:32:26

cocos2dx spine之二 :spine变色的相关文章

Cocos2d-x layout (二)

相对某个控件进行布局 Size widgetSize = Director::getInstance()->getWinSize(); Text* alert = Text::create("Layout", "fonts/Marker Felt.ttf", 30 ); alert->setColor(Color3B(159, 168, 176)); alert->setPosition(Point(widgetSize.width / 2.0f,

10.4 Excel方式二维变色提示的表格

在10.3节中,整行变色提求鼠标指针经过的效果已经完成了,接下来继续改进它.实现类似于Excel的行列的二维提示,效果如图1所示,当鼠标指针经过某一个单元格时.相应的列头和行头单元格会同时变色. 实例文件位于网页学习网CSS教程资源的“第10章\02\pretty-3.htm”. 图1 表格的行列二维变色提示 注意:本案例需要不少JavaScript编程的配合,如果读者缺乏相应基础,学起来可能会有一点困难. 一.改造CSS代码 首先改造CSS设置,这个效果单纯使用CSS是无法实现的,必须要使用j

cocos2dx spine之一 :spine缓存 (c++ &amp; lua)

cocos2dx版本为3.10 1.在使用spine的过程中,发现了一个比较严重的问题:每次创建SkeletonAnimation的时候都会很卡,即使是使用同一个骨骼数据skeletonData. 跟踪代码发现,在每次调用函数spine::SkeletonAnimation::createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);的时候都需要重新

Spine学习二 -播放Spine动画

要想播放一个Spine动画,必须要在一个物体上绑定一个Spine播放的组件,这里暂时使用SkeletonAnimation组件. 然后就是编写动画的控制脚本. 这里提一个特性: [SpineAnimation]:这个特性可以获取到SkeletonAnimation组件中绑定的 Spine资源的所有 动作名字, [SpineAnimation] public string runAnimationName; 其次,官方说了一个注意点,那就是最好不要在 Start()之前使用 AnimationSt

Spine学习五- spine动画融合

在许多地方,都需要用到动画融合,unity的新版动画系统已经能够很方便的进行动画融合,那么使用spine的动画状态机的情况下,如何来进行动画融合呢? 官方有两种方案,一种是使用混合动作实现,另一种是使用spine的动画状态机实现,这里讲解一下状态机的实现: 1 public class cartoonCombine : MonoBehaviour { 2 3 SkeletonAnimation skeletonAnimation; 4 AnimationStateData stateData;

如何实现 Excel方式二维变色提示的 m*n 表格

此代码当m≠n 时,有问题.暂时还未解决此问题. 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 &

VS搭建lua开发环境和LuaBridge注册c++类(cocos2dx项目)(二)

这篇我们注册2个类来测试下LuaBridge的使用,在开始之前我们先在上篇的基础上来用C++调用下lua的函数.我们添加一个main.lua文件到资源文件夹下.lua代码如下: 1 function init() 2 print(1) 3 end 然后,在initscript()函数添加以下代码: 1 std::string filePath = FileUtils::getInstance()->fullPathForFilename("main.lua"); 2 int re

cacti系列之安装篇 LNMP+cacti+spine (一)

大纲 一.cacti简述 二.配置nginx.php.mysql 三.cacti + spine 搭建 一.简述 Cacti基于SNMP采集数据源,通过rrdtool储存数据并绘图呈现给用户.SNMP获取的数据源为.rrd文件,存储在cacti目录下的rra目录. 工作流程如图: 二.配置nginx.php.mysql(忽略LNMP环境部署过程) 1.配置nginx #在nginx中加入一个server [[email protected]_6.213 cacti-spine-0.8.8h]$v

Spine 实用技巧大全

转载请保留原始链接:Spine 2D骨骼动画 http://qgc.qq.com/222369324/t/18 1.账号登出 log Out 2.Spine 如何调整帧速率 3.Spine 如何导出 (透明度有问题,图片有黑边的,看这个) 4.Spine 如何设置参考图片 5.Spine 版本回退 6.Spine 图片显示出错的处理方法 7.Spine 的自动保存 8.Spine 里改变显示层级 DrawOrder 9.Spine 的帧速率 30fps 默认 10.创建骨骼时直接添加图片子为物体