Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Utils/MaterialUtils.ShaderSources.cs
Original file line number Diff line number Diff line change
@@ -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;
}
""";
}
}
66 changes: 33 additions & 33 deletions Utils/MaterialUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,54 @@ namespace STS2RitsuLib.Utils
/// Factory helpers for Godot materials that mirror vanilla game shaders.
/// 用于镜像原版游戏着色器的 Godot 材质工厂辅助方法。
/// </summary>
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";

private static NoiseTexture2D? _vanillaDoomBarNoiseTexture;
private static ShaderMaterial? _unmodulatedHsvMaterial;

private static Shader? GameHsvShader => (Shader?)GD.Load<Shader>(HsvShaderPath)?.Duplicate();
private static Shader? _gameHsvShader;
private static Shader GameHsvShader => _gameHsvShader ??= GD.Load<Shader>(HsvShaderPath) ?? throw new InvalidOperationException($"Failed to load HSV shader ({HsvShaderPath}).");

private static Shader? GameDoomBarShader => (Shader?)GD.Load<Shader>(DoomBarShaderPath)?.Duplicate();
private static Shader? _gameDoomBarShader;
private static Shader? GameDoomBarShader => _gameDoomBarShader ??= GD.Load<Shader>(DoomBarShaderPath) ?? throw new InvalidOperationException($"Failed to load doom bar shader ({DoomBarShaderPath}).");

private static Shader? _replaceHueShader;
private static Shader ReplaceHueShader => _replaceHueShader ??= new Shader
{
Code = ReplaceHueShaderSource,
};

private static NoiseTexture2D VanillaDoomBarNoiseTexture =>
_vanillaDoomBarNoiseTexture ??= CreateVanillaDoomBarNoiseTexture();

/// <summary>
/// Builds a <c>ShaderMaterial</c> 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.
/// 使用一个自定义着色器构建 <c>ShaderMaterial</c>,该着色器将输入纹理的色调替换为调用方指定的 RGB 颜色,
/// 同时保留原始亮度和饱和度。适用于替换色调,例如给原版卡框换色。
/// 参数r,g,b的范围是0-1,brightness的范围是0-2,默认值为1。
/// </summary>
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;
}

/// <summary>
/// Builds a <c>ShaderMaterial</c> using the game's HSV shader with the given RGB parameters.
/// 使用游戏的 HSV 着色器和给定 RGB 参数构建 <c>ShaderMaterial</c>。
/// </summary>
[Obsolete("Prefer MaterialUtils.CreateReplaceHueShaderMaterial instead.")]
public static ShaderMaterial CreateRgbShaderMaterial(float r, float g, float b)
{
var max = Math.Max(r, Math.Max(g, b));
var min = Math.Min(r, Math.Min(g, b));
var delta = max - min;

float h = 0;
if (delta != 0)
{
if (Mathf.IsEqualApprox(max, r)) h = (g - b) / delta + (g < b ? 6 : 0);
else if (Mathf.IsEqualApprox(max, g)) h = (b - r) / delta + 2;
else h = (r - g) / delta + 4;
h /= 6;
}

var s = max == 0 ? 0 : delta / max;
return CreateHsvShaderMaterial(h, s, max);
return CreateReplaceHueShaderMaterial(r, g, b);
}

/// <summary>
Expand All @@ -50,18 +62,10 @@ public static ShaderMaterial CreateRgbShaderMaterial(float r, float g, float b)
/// </summary>
public static ShaderMaterial CreateHsvShaderMaterial(float h, float s, float v)
{
var shader = GameHsvShader ??
throw new InvalidOperationException($"Failed to load HSV shader ({HsvShaderPath}).");

var material = new ShaderMaterial
{
Shader = shader,
};

var material = new ShaderMaterial { Shader = GameHsvShader };
material.SetShaderParameter("h", h);
material.SetShaderParameter("s", s);
material.SetShaderParameter("v", v);

return material;
}

Expand All @@ -80,7 +84,7 @@ public static ShaderMaterial CreateHsvShaderMaterial(float h, float s, float v)
public static ShaderMaterial CreateUnmodulatedHsvShaderMaterial()
{
_unmodulatedHsvMaterial ??= CreateHsvShaderMaterial(0f, 1f, 1f);
return (ShaderMaterial)_unmodulatedHsvMaterial.Duplicate();
return _unmodulatedHsvMaterial;
}

/// <summary>
Expand All @@ -100,11 +104,7 @@ public static ShaderMaterial CreateDoomBarShaderMaterial(GradientTexture1D gradi
{
ArgumentNullException.ThrowIfNull(gradientTexture);

var shader = GameDoomBarShader;
if (shader == null)
throw new InvalidOperationException($"Failed to load doom bar shader ({DoomBarShaderPath}).");

var material = new ShaderMaterial { Shader = shader };
var material = new ShaderMaterial { Shader = GameDoomBarShader };
material.SetShaderParameter("noise_tex", VanillaDoomBarNoiseTexture);
material.SetShaderParameter("gradient_tex", gradientTexture);
return material;
Expand Down