实现 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 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值

相关推荐
PedroQue9925 分钟前
0.5.0重磅发布:动画插件+H5体验全面优化
前端·uni-app
金虹桥程序员29 分钟前
【动画】 📚 CSS 动画与 GPU 合成层优化全链路指南
前端·面试
程序员老赵33 分钟前
Docker 部署填鸭表单完整教程:搭建私有化问卷与表单收集平台
前端·docker·开源
无限压榨切图仔1 小时前
向量库能搜到内容,为什么还不算学会 RAG
前端·agent
mmsx1 小时前
MapLibre 实战 11|用户说"我的地块丢了":一个 sealed class 图层模型,和四个让我重构三版的坑
android·前端·开源
xqchen1 小时前
代码差异可视化方案选型:diff2html 实战指南
前端
名字还没想好☜1 小时前
用 Zustand 做 React 全局状态管理:告别 Context 重渲染,3 个实战模式与持久化
前端
IMPYLH1 小时前
HTML 的 <section> 元素
前端·html
X1A0RAN1 小时前
密匣 PsdKeep:一个属于你自己的 Chrome 账号记事本
前端·chrome
529宝宝起名网2 小时前
用 Python 开发历史名字查询与起名灵感工具:从古籍人物数据库到名字文化故事生成
开发语言·前端·python