Shader学习案例 色相转换

在学习Shader的过程中做的小案例,在 Creator v2.4.10 使用实现了物体的色相转换。

根据 这位的思路 写的:在片元着色器中,采样读取当前点的颜色后,将颜色的rgb转换为hsv。然后取色相判定,加一个值,让它转换。

changeColor.mtlchangeColor.effect用于色相转换

创建 SpriteNode 节点, 给其添加上图和changeColor.mtl材质。


CCEffect

      properties:
        targetHue: { value: 240, editor: { tooltip: '目标色相值'} }
        hueRange: { value: 60, editor: { tooltip: '色相范围'} }
        hueRotationAngle : { value: 120, editor: { tooltip: '色相旋转角度'} }

片段着色器 fs

声明部分:

  uniform HueColor {
    float targetHue;
    float hueRange;
    float hueRotationAngle;
  };

  vec3 rgbToHsv(vec3 rgb);  // 将 RGB 转换为 HSV
  vec3 hsvToRgb(vec3 hsv);  // 将 HSV 转换回 RGB

入口函数:

void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      o = texture2D(texture, v_uv0);
    #endif

    vec3 hsv = rgbToHsv(vec3(o.r, o.g, o.b));

    float minHue = targetHue - hueRange;
    float maxHue = targetHue + hueRange;

    if (hsv.x >= minHue && hsv.x <= maxHue) {
        hsv.x += hueRotationAngle; // 色相旋转

        if (hsv.x > 360.0) {
            hsv.x -= 360.0;
        }
    }
    vec3 newColor = hsvToRgb(hsv);

    gl_FragColor = vec4(newColor, o.a) *  v_color;
  }

rgbToHsv函数:

vec3 rgbToHsv(vec3 rgb) {
    float r = rgb.r;
    float g = rgb.g;
    float b = rgb.b;

    float minVal = min(min(r, g), b);
    float maxVal = max(max(r, g), b);
    float delta = maxVal - minVal;

    float h = 0.0;
    float s = 0.0;
    float v = maxVal;

    if (delta != 0.0) {
        if (maxVal == r) {
            h = (g - b) / delta;
        } else if (maxVal == g) {
            h = 2.0 + (b - r) / delta;
        } else {
            h = 4.0 + (r - g) / delta;
        }

        h *= 60.0;
        if (h < 0.0) {
            h += 360.0;
        }

        s = delta / maxVal;
    }

    return vec3(h, s, v);
  }

hsvToRgb函数:

vec3 hsvToRgb(vec3 hsv) {
      float h = hsv.x;
      float s = hsv.y;
      float v = hsv.z;

      float c = v * s;
      float x = c * (1.0 - abs(mod(h / 60.0, 2.0) - 1.0));
      float m = v - c;

      vec3 rgb;
      if (h >= 0.0 && h < 60.0) {
          rgb = vec3(c, x, 0.0);
      } else if (h >= 60.0 && h < 120.0) {
          rgb = vec3(x, c, 0.0);
      } else if (h >= 120.0 && h < 180.0) {
          rgb = vec3(0.0, c, x);
      } else if (h >= 180.0 && h < 240.0) {
          rgb = vec3(0.0, x, c);
      } else if (h >= 240.0 && h < 300.0) {
          rgb = vec3(x, 0.0, c);
      } else {
          rgb = vec3(c, 0.0, x);
      }

      return rgb + vec3(m);
  }

编辑器参数:

02

效果:

01

5赞

这一招我在实战中用过。之前有个项目,有红蓝两套阵营的角色帧动画。用shader变色的方法可以节省一半的资源。
另外,分享个经验,对于角色的红蓝转换,资源最好是蓝色阵营,然后用shader变红,这是因为人物的皮肤一般会有一部分区域偏红,红转蓝容易出瑕疵,蓝转红比较好用。

5赞

经验啊经验

changeColor.effect 发一下 lz

effect所用到的代码都贴出来了,创建effect后改改就行了

hsl的参数通过node color的rgb传递。

这样就可以合批了,是吗

这种应该不能合批吧

大佬 effect文件能完整贴一下吗 :joy:没有shader基础,直接用贴出来的代码报错 :cold_sweat:
是不是少了一个vs的

因为没有修改 vs


一般就是创建一个 材质(Material) 挂上 创建的 Effect ,然后修改就行了。

屏幕截图 2024-09-29 093327


effect文件:
changeColor.zip (1.3 KB)

好的 多谢大佬 :pray:
这个能实现,传一个颜色区间值和目标颜色值,改变对应的颜色吗?

可以传入 目标值(要变换的颜色值),和要变换的角度来实现,不是有3个参数吗,你修改试试吧。

  • targetHue 目标值
  • hueRange 目标值范围
  • hueRotationAngle 要旋转的角度

使用的色相(hue)那个色盘,改变角度实现的颜色变化。

222158mn999x2271xdy82y

好,感谢 :pray:

这个好!非常实用,感谢大佬