同一个2d图片使用shader渲染出不同颜色图片的性能可行性研究

需求背景:产品希望美术只输出一份素材,比如一棵树,程序渲染出不同的美术效果,实现春/夏/秋/冬/低等级/高等级等不同的情况。

技术方案:目前想到的是通过 shader模拟实现类似ps的 调整色相/饱和度/色彩平衡/亮度/对比度等算法,最终修改像素颜色实现。

问题:请教各位大佬,shader是实时计算渲染的,这个在性能上是否可行?

模拟这个场景测试:

200 draw call 图片平均尺寸 505*505
小米11pro 微信扫码: 原图fps约50-60 shader处理的fps约36-37
iphone12pro 微信扫码: 原图fps约42-47 shader处理的fps约25-32

个人认为加了shader处理的这个实时性能有点勉强,因为现在是项目初期,不好预测最终 同屏会达到多少drawcall。

如果实时渲染性能上无法满足,设想的一种方法是,把shader处理1次的图片存储在本地(手机存储卡上),下次读取就直接用处理过的图片显示出来。这种方案是否存在什么弊端或风险?

shader处理的大概算法如下:

vec3 liangDuDuiBiDu(in vec4 co,float B,float C){
B = B / 200.;
C = C / 200.;
B = B + 1.;
C = C + 1.;
vec3 finnalColor = co.rgb * B;

float luminance = 0.2125 * co.r + 0.7154 * co.g + 0.0721 * co.b;
vec3 luminanceColor = vec3(luminance);
finnalColor = lerp(luminanceColor,finnalColor,1.);
vec3 avgColor = vec3(0.5);
finnalColor = lerp(avgColor,finnalColor,C);

return finnalColor;

}
vec3 seCaiPingHeng(in vec4 coA, vec3 yy, vec3 zj, vec3 gg){
float BB = 255.;
vec3 u_shadows = vec3(yy[0]/BB, yy[1]/BB, yy[2]/BB); // 阴影的色彩平衡调整量
vec3 u_midtones = vec3(zj[0]/BB, zj[1]/BB, zj[2]/BB); // 中间调的色彩平衡调整量
vec3 u_highlights = vec3(gg[0]/BB, gg[1]/BB, gg[2]/BB); // 高光的色彩平衡调整量

// 阴影、中间调、高光的分界点
float SHADOWS_THRESHOLD = 0.3333;
float HIGHLIGHTS_THRESHOLD = 0.6666;

// 计算亮度(Luminance)
float luminance = 0.2126 * coA.r + 0.7152 * coA.g + 0.0722 * coA.b;

// 定义色彩调整的权重
vec3 adjustment = vec3(0.0);
float weight = 0.0;


// 判断亮度区域并应用不同的色彩平衡调整
if (luminance < SHADOWS_THRESHOLD) {
    // 阴影部分
    weight = smoothstep(0.0, SHADOWS_THRESHOLD, luminance);
    adjustment = u_shadows; // 应用阴影调整   + vec3(0.0,1.0,1.0)
} 
else if (luminance < HIGHLIGHTS_THRESHOLD) {
    // 中间调部分
    weight = smoothstep(SHADOWS_THRESHOLD, HIGHLIGHTS_THRESHOLD, luminance);
    adjustment = u_midtones; // 应用中间调调整   + vec3(1.0,0.0,1.0)
} 
else {
    // 高光部分
    weight = smoothstep(HIGHLIGHTS_THRESHOLD, 1.0, luminance);
    adjustment = u_highlights; // 应用高光调整   + vec3(1.0,1.0,0.0)
}
 // 应用色彩平衡调整
vec3 rgb = coA.rgb;
vec3 balancedColor = mix(rgb, rgb + adjustment, weight); 

// 保持亮度不变(可选),避免颜色反转
float originalLuminance = calculateLuminance(rgb);
float newLuminance = calculateLuminance(balancedColor);
balancedColor.rgb *= originalLuminance / (newLuminance + 0.0001); // 防止除0

return balancedColor;    }
1赞

让美术出图吧,和技术无关。。。

美术出图要出多份,程序能实现的话美术出图不是最佳方案哦

可以,我已实现就是要调参数。

大佬说的可以,是指shader实时渲染,还是渲染1次图存本地?

Shader实时。

提前实时渲染出来,存储到本地晒

这个我记得有大佬分享贴子

PS 有一个一种文件格式 叫look up table 可以用来渲染。

明白,我尝试了lut的方式,然后有个疑问,cocos是配置在相机里面的,似乎针对的是整个界面进行处理(不知道我的理解对不对),而我只想针对特定的图进行处理,请问能实现吗?

配置在相机就是后处理,对rendertexture。理解原理,肯定也能对单独图

如果 shader 可以绑定摄像机和图片

用shader实现了 look up table 对单个Sprite图片变色,这个方案优点是完全100%还原美术效果。

经过测试, 安卓下性能非常差,具体数据如下:

200 draw call 图片平均尺寸 505*505
小米11pro 微信扫码: 原图fps约50-60 shader+lut处理的fps约22
iphone12pro 微信扫码: 原图fps约42-47 shader+lut处理的fps约30-36

lut解析算法 直接copy官方的代码:

vec4 rgba = texture(cc_spriteTexture, uv0);
if(rgba.a < 0.01)
return rgba;

vec3 orgColor = rgba.rgb;
float bValue = (orgColor.b * 255.0) / 4.0;
vec2 mulB = clamp(floor(bValue) + vec2(0.0, 1.0), 0.0, 63.0);
vec2 row = floor(mulB / 8.0 + EPS);
vec4 row_col = vec4(row, mulB - row * 8.0);
vec4 lookup = orgColor.ggrr * (63.0 / SIZE) + row_col * (TOTAL / SIZE) + (0.5 / SIZE);

float b1w = bValue - mulB.x;

vec3 sampled1 = texture(lutTexture, lookup.zx).rgb;
vec3 sampled2 = texture(lutTexture, lookup.wy).rgb;
vec3 sampled2 = sampled1;

vec4 fragColor = vec4(mix(sampled1, sampled2, b1w), rgba.a);

return fragColor;

}

我个人觉得能到 30fps以上勉强能接受, 各位大佬有什么建议呢?

我看一下 这个代码应该不会太大性能问题,是不是 用shader过后 影响了合批。

现在是demo测试,没有合图,就是 210张平均尺寸 505*505的图片 显示在舞台。

texture(lutTexture, lookup.wy).rgb 采样这里比较耗时,这个有办法优化吗?

这句话就是像素采样,你渲染普通图片也有呀。

明白,是否还能缓存或是怎样?

我尝试把 lut纹理图缩小,前面用的是 512*512,换成256*256, 同等drawcall环境下,fps由22提升至49,效果很明显。ios下没有提升依旧是 30-36。

这样的性能似乎可以接受了。

你可以考虑把通用计算放到顶点渲染。

好的,我再研究下怎么写…