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

相关推荐
满怀10156 分钟前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
东锋1.317 分钟前
前端动画库 Anime.js 的V4 版本,兼容 Vue、React
前端·javascript·vue.js
满怀101530 分钟前
【Flask全栈开发指南】从零构建企业级Web应用
前端·python·flask·后端开发·全栈开发
小杨升级打怪中1 小时前
前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
前端·webpack·node.js
Yvonne爱编码1 小时前
CSS- 4.4 固定定位(fixed)& 咖啡售卖官网实例
前端·css·html·状态模式·hbuilder
SuperherRo2 小时前
Web开发-JavaEE应用&SpringBoot栈&SnakeYaml反序列化链&JAR&WAR&构建打包
前端·java-ee·jar·反序列化·war·snakeyaml
大帅不是我2 小时前
Python多进程编程执行任务
java·前端·python
前端怎么个事2 小时前
框架的源码理解——V3中的ref和reactive
前端·javascript·vue.js
Ciito2 小时前
将 Element UI 表格元素导出为 Excel 文件(处理了多级表头和固定列导出的问题)
前端·vue.js·elementui·excel
不爱吃饭爱吃菜3 小时前
uniapp微信小程序一键授权登录
前端·javascript·vue.js·微信小程序·uni-app