实现 creator spine 根据灰度渐变着色

项目背景

想要将spine整体进行变色,大概效果如下图

效果分析

简单说就是将整体效果灰度化后,然后进行目标颜色的着色,具体着色算法为根据灰度值进行插值,也就是从mixColor->white渐变。

实现过程

像素灰度

算法想好了,接下来就开始实现, 先置灰

ini 复制代码
float gray = dot(vec3(finalColor.r, finalColor.g, finalColor.b), vec3(0.2126, 0.7152, 0.0722));
gray = max(min(1.0,gray),0.0);
finalColor.r = finalColor.g = finalColor.b = gray;
gl_FragColor = finalColor;

效果和预期的一致,变成了灰度图

着色

接下来就是着色

ini 复制代码
 float gray = dot(vec3(finalColor.r, finalColor.g, finalColor.b), vec3(0.2126, 0.7152, 0.0722));
 gray = max(min(1.0,gray),0.0);
 finalColor.r = mix(mixColor.r, 1.0, gray);
 finalColor.g = mix(mixColor.g, 1.0, gray);
 finalColor.b = mix(mixColor.b, 1.0, gray);
 gl_FragColor = finalColor;

发现透明区域出现了mixColor

这个很好理解,因为透明区域计算出来的gray为0,mix之后就是mixColor

discard处理透明区域

解决办法:可以将透明度为0的丢弃掉,这种办法会导致出现很多毛边

if ( finalColor.a <= 0.0 ) discard;

预乘出现黑边

所以废弃discard这种方案,可以采用alpha预乘

ini 复制代码
finalColor.r = mix(mixColor.r, 1.0, gray) * texColor.a;
finalColor.g = mix(mixColor.g, 1.0, gray) * texColor.a;
finalColor.b = mix(mixColor.b, 1.0, gray) * texColor.a;

可以看到效果有明显的提升,但是发现嘴巴附近有黑边,这个黑边主要就是来自边缘的过渡色

ALPHA_TEST

ALPHA_TEST可以缓解这个问题,但是会带来锯齿问题

因为ALPHA_TEST本质也是discard

c 复制代码
#if USE_ALPHA_TEST 
    uniform float alphaThreshold; 
#endif 

void ALPHA_TEST (in vec4 color) 
{ 
#if USE_ALPHA_TEST 
    if (color.a < alphaThreshold) discard; 
#endif 
}

void ALPHA_TEST (in float alpha) 
{ 
#if USE_ALPHA_TEST 
    if (alpha < alphaThreshold) discard;
#endif 
}

基于源纹理处理

如果再顺着这个思路解决下去,好像到了死胡同,卡了我好几天,最后还是热心网友大华的帮助,顺利解决了这个问题,感谢大华哥

以上的所有处理都是基于最终计算(finalColor)的颜色处理的,再次理清需求,发现我们要基于纹理的颜色处理。

ini 复制代码
 float gray = dot(vec3(texColor.r,texColor.g, texColor.b), vec3(0.2126, 0.7152, 0.0722));
 gray = max(min(1.0,gray),0.0);
 float r = mix(mixColor.r, 1.0, gray);
 float g = mix(mixColor.g, 1.0, gray);
 float b = mix(mixColor.b, 1.0, gray);
 vec3 grayColor = vec3(r,g,b) * texColor.a; // 预乘
 finalColor = vec4(grayColor, texColor.a);

发现嘴巴部分还是有黑边

spine 纹理alpha预乘

检查完毕逻辑,发现的确算法是符合预期的,那么问题只可能出在纹理上,在spine中有这个选项,可以将输出的纹理预乘。

尝试还原下纹理颜色,基于纹理真实的颜色进行灰度

ini 复制代码
vec3 color = texColor.rgb / texColor.a;
float gray = dot(color, vec3(0.2126, 0.7152, 0.0722));

至此,黑边也消失了,效果也就符合我们的预期了

v_light 处理

最后,我们还需要还原spine原有的颜色处理逻辑,也就是要增加灯光的处理。

下图是v_light的不同效果,可以看到阴影处的不同,没有按照预期着色

解决办法也简单,对原始颜色处理下v_light就可以了

其他地方的预乘

spine组件的预乘

查看引擎源码,你会发现这个预乘是作用于blend的

js 复制代码
let srcBlendFactor = this.premultipliedAlpha 
    ? cc.gfx.BLEND_ONE 
    : cc.gfx.BLEND_SRC_ALPHA;
let dstBlendFactor = cc.gfx.BLEND_ONE_MINUS_SRC_ALPHA;
baseMaterial.setBlend(
    true,
    cc.gfx.BLEND_FUNC_ADD,
    srcBlendFactor, srcBlendFactor,
    cc.gfx.BLEND_FUNC_ADD,
    dstBlendFactor, dstBlendFactor
);

图片资源的预乘属性

和spine的预乘一个道理,都是对纹理图片的修改

预乘的意义

Premultiplied Alpha 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值

相关推荐
Agent世界10 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo10 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
某哈压力大10 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳10 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半11 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳11 小时前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
deli00711 小时前
写满 200GB 后 SSD 为什么掉到 95MB/s?SLC 缓存模型实测
前端
曦樂~12 小时前
【Web Development】03JavaScript 基础和事件
开发语言·前端·javascript
deli00712 小时前
HTTPS 的锁是怎么挂上的?TLS 1.3 握手 6 步实测
前端
程序员大阳12 小时前
副队长ES6+教程(1)--ES6+初识
前端·ecmascript·es6