diff --git a/Utils/MaterialUtils.ShaderSources.cs b/Utils/MaterialUtils.ShaderSources.cs new file mode 100644 index 0000000..038fa4b --- /dev/null +++ b/Utils/MaterialUtils.ShaderSources.cs @@ -0,0 +1,38 @@ +namespace STS2RitsuLib.Utils +{ + public static partial class MaterialUtils + { + private const string ReplaceHueShaderSource = """ + shader_type canvas_item; + + const vec3 LUMA_WEIGHTS = vec3(0.2126, 0.7152, 0.0722); + const float EPSILON = 1e-7; + const float MAX_COLOR_GAIN = 1.12; + + uniform vec3 target_color : source_color = vec3(1.0); + uniform float brightness : hint_range(0.0, 2.0) = 1.0; + + varying vec4 modulate_color; + + void vertex() { + modulate_color = COLOR; + } + + void fragment() { + vec4 col = texture(TEXTURE, UV); + + float max_rgb = max(max(col.r, col.g), col.b); + float min_rgb = min(min(col.r, col.g), col.b); + float value = max_rgb * brightness; + float saturation = (max_rgb - min_rgb) / (max_rgb + EPSILON); + + float target_value = max(max(target_color.r, target_color.g), target_color.b); + vec3 target_hue = target_color / max(target_value, EPSILON); + float color_gain = min(1.0 / max(dot(target_hue, LUMA_WEIGHTS), EPSILON), MAX_COLOR_GAIN); + + vec3 final = mix(vec3(value), target_color * value * color_gain, saturation); + COLOR = vec4(final, col.a) * modulate_color; + } + """; + } +} diff --git a/Utils/MaterialUtils.cs b/Utils/MaterialUtils.cs index 877e3ff..1ffdf49 100644 --- a/Utils/MaterialUtils.cs +++ b/Utils/MaterialUtils.cs @@ -6,7 +6,7 @@ namespace STS2RitsuLib.Utils /// Factory helpers for Godot materials that mirror vanilla game shaders. /// 用于镜像原版游戏着色器的 Godot 材质工厂辅助方法。 /// - public static class MaterialUtils + public static partial class MaterialUtils { private const string HsvShaderPath = "res://shaders/hsv.gdshader"; private const string DoomBarShaderPath = "res://scenes/combat/doom_bar.gdshader"; @@ -18,13 +18,37 @@ public static class MaterialUtils private static Shader? GameDoomBarShader => (Shader?)GD.Load(DoomBarShaderPath)?.Duplicate(); + private static Shader? _replaceHueShader; + private static Shader ReplaceHueShader => _replaceHueShader ??= new Shader + { + Code = ReplaceHueShaderSource, + }; + private static NoiseTexture2D VanillaDoomBarNoiseTexture => _vanillaDoomBarNoiseTexture ??= CreateVanillaDoomBarNoiseTexture(); + /// + /// Builds a ShaderMaterial using a custom shader that replaces the hue of the input texture + /// with a caller-specified RGB color, while preserving the original brightness and saturation. + /// Suitable for replacing hues when using vanilla card frames. + /// parameters r, g, b are in the range 0-1, brightness is in the range 0-2 with a default of 1. + /// 使用一个自定义着色器构建 ShaderMaterial,该着色器将输入纹理的色调替换为调用方指定的 RGB 颜色, + /// 同时保留原始亮度和饱和度。适用于替换色调,例如给原版卡框换色。 + /// 参数r,g,b的范围是0-1,brightness的范围是0-2,默认值为1。 + /// + public static ShaderMaterial CreateReplaceHueShaderMaterial(float r, float g, float b, float brightness = 1f) + { + var material = new ShaderMaterial { Shader = ReplaceHueShader }; + material.SetShaderParameter("target_color", new Vector3(r, g, b)); + material.SetShaderParameter("brightness", brightness); + return material; + } + /// /// Builds a ShaderMaterial using the game's HSV shader with the given RGB parameters. /// 使用游戏的 HSV 着色器和给定 RGB 参数构建 ShaderMaterial。 /// + [Obsolete("Prefer MaterialUtils.CreateReplaceHueShaderMaterial instead.")] public static ShaderMaterial CreateRgbShaderMaterial(float r, float g, float b) { var max = Math.Max(r, Math.Max(g, b));