最近在做国内版本的合规工作,其中一项要求是游戏里不能出现代表血腥的红色(嗯?),鉴于红色是一个很常见的颜色,我就在思考是否可以通过后期效果的方式将红色转为其他颜色,以节约巨大的工作量。
做了一些尝试之后,我做了一个版本,还算符合要求,以下是我的解决方案:
(最后面附上了效果对比)
C# 脚本
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode, ImageEffectAllowedInSceneView]
public class ColorSwapEffect : MonoBehaviour
{
[SerializeField]
private Material effectMaterial;
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (effectMaterial != null)
{
Graphics.Blit(src, dest, effectMaterial);
}
else
{
Graphics.Blit(src, dest);
}
}
}
Shader
fixed4 _Color = fixed4(60.0f / 255.0f, 69.0f / 255.0f, 85.0f / 255.0f, 1);
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 newCol = col;
// swap colors
fixed offset_g = col.r - col.g;
fixed offset_b = col.r - col.b;
if (offset_g < 0)
{
offset_g = 0;
}
if (offset_b < 0)
{
offset_b = 0;
}
fixed offset_gv = col.g /col.r;
fixed offset_bv = col.b / col.r;
fixed offset = ( offset_g + offset_b / (col.r * 2));
fixed luminance = dot(col.rgb, fixed3(0.299, 0.587, 0.114));
if (offset > 0.2)
{
if (offset_gv < 0.75 && offset_bv < 0.75 && offset > 0.1)
{
newCol = lerp(col, _Color, 0.2f);
}
if (offset_gv < 0.65 && offset_bv < 0.65 && offset > 0.1)
{
newCol = lerp(col, _Color, 0.4f);
}
if (offset_gv < 0.55f && offset_bv < 0.55f && offset > 0.2f)
{
newCol = lerp(col, _Color, 0.6f);
}
if (offset_gv < 0.45f && offset_bv < 0.45f && offset > 0.2f)
{
newCol = lerp(col, _Color, 0.9f);
}
if (offset_gv < 0.35f && offset_bv < 0.35f && offset > 0.2f)
{
newCol = lerp(col, _Color, 1);
}
}
fixed newluminance = dot(newCol.rgb, fixed3(0.299, 0.587, 0.114));
newCol.rgb *= luminance / newluminance;
col = lerp(col, newCol, _ColorSwap);
return col;
}
其实这个问题的本质就是如何识别红色,以及如何将其转换为另外一个颜色的同时,不显著的影响整个画面的美观性,我的思路就是从rgb的相对值出发,如果r的值显著大于了gb两个颜色,则将其视为红色,并根据差别的大小,阶段式地用不同的插值参数。
最终再基于原来的亮度,对新的色彩亮度进行还原。
失去红色之后,整个画面会变得大伤元气,有一种阴森的感觉,但相比起将游戏中的素材大大改一遍相比
变色前
变色后