diff --git a/Assets/Scripts/umamusume/Gallop/Live/StageController.cs b/Assets/Scripts/umamusume/Gallop/Live/StageController.cs
index 12df663..0e06060 100644
--- a/Assets/Scripts/umamusume/Gallop/Live/StageController.cs
+++ b/Assets/Scripts/umamusume/Gallop/Live/StageController.cs
@@ -2,6 +2,7 @@ using Gallop.Live.Cutt;
using System;
using System.Collections.Generic;
using UnityEngine;
+using UnityEngine.Rendering;
namespace Gallop.Live
@@ -106,6 +107,9 @@ namespace Gallop.Live
}
}
+ // 光柱/闪光类 shader 的混合状态被 bundle 材质里的 URP Lit 属性表压成了不透明,先修回来。
+ RestoreAuthoredBlendState();
+
// 舞台自带 Animation 的播放(原版由缺失的 AnimationObjectController 负责)。
StageAnimationPlayer.Setup(this);
@@ -113,6 +117,85 @@ namespace Gallop.Live
StageMissingScriptCensus.Dump(this);
}
+ ///
+ /// 把材质的 `_SrcBlend`/`_DstBlend` 恢复成 **shader 自己声明的默认值**。
+ ///
+ /// 为什么必须做:Gallop 的光柱类 shader 把混合状态写成 `Blend [_SrcBlend] [_DstBlend]`,
+ /// 也就是从材质属性里读。而 live10149 的 **26/26 个材质**存的都是
+ /// `_SrcBlend=One, _DstBlend=Zero`(= 不透明覆盖),并且每一个的 float 表里都齐刷刷带着
+ /// `_WorkflowMode`/`_Surface`/`_ClearCoatMask`/`_Smoothness`/`_Blend` —— 那是 **URP Lit 的
+ /// 属性表**,整批盖上去的,One/Zero 正是 URP Lit 的不透明预设,不是作者意图。
+ ///
+ /// 作者意图是可查的:全库 499 个 shader 里有 16 个声明了这两个属性,其中 **14 个的
+ /// 声明默认值是 `One/One`(加法混合)** —— LightBlinkBlend、LightBlend、StageBeamLight
+ /// 三兄弟、StageLightBlink{Cutoff,Fadeout}、StageProjectorBlend 三兄弟、
+ /// MirrorBallProjector 等,全是发光/投影的那一批。
+ ///
+ /// 后果:加法混合下 `_BlinkLightColor` 为黑 = 什么都不加 = 光柱不可见;被压成不透明后,
+ /// 同一个黑色就变成一堵实心黑墙。ground wash light 扫动时那几片「转来转去的黑楔子」
+ /// 就是这么来的,`LIVE_TRACKS.md` 里「小灯泡渲染成黑块」多半也是同一根因。
+ ///
+ /// 这个修法是自限的:对本来就该不透明的 shader,声明默认值就是 One/Zero,写回去等于没动。
+ /// ⚠ 只改 shader **确实声明了**的属性 —— 材质存档表不可信,shader 才是准。
+ ///
+ private void RestoreAuthoredBlendState()
+ {
+ var done = new HashSet();
+ var changed = new List();
+
+ foreach (var r in GetComponentsInChildren(true))
+ {
+ foreach (var mat in r.sharedMaterials)
+ {
+ if (mat == null || mat.shader == null || !done.Add(mat)) continue;
+
+ string before = null, after = null;
+ foreach (string prop in kBlendProps)
+ {
+ if (!TryGetShaderDefaultFloat(mat.shader, prop, out float authored)) continue;
+ float current = mat.GetFloat(prop);
+ if (Mathf.Approximately(current, authored)) continue;
+
+ before = $"{before}{prop}={current} ";
+ after = $"{after}{prop}={authored} ";
+ mat.SetFloat(prop, authored);
+ }
+
+ if (after != null)
+ changed.Add($"{mat.name}[{mat.shader.name}] {before}→ {after}");
+ }
+ }
+
+ if (changed.Count == 0)
+ {
+ Debug.Log($"[StageBlend] {done.Count} 个材质的混合状态与 shader 声明一致,无需修正");
+ return;
+ }
+
+ Debug.Log($"[StageBlend] {changed.Count}/{done.Count} 个材质的 _SrcBlend/_DstBlend 与 shader 声明不符," +
+ $"已恢复成 shader 的默认值:\n " + string.Join("\n ", changed));
+ }
+
+ private static readonly string[] kBlendProps = { "_SrcBlend", "_DstBlend" };
+
+ /// shader 声明的默认值。材质没有这个属性、或类型不是 Float/Range 时返回 false。
+ private static bool TryGetShaderDefaultFloat(Shader shader, string propName, out float value)
+ {
+ value = 0f;
+ int count = shader.GetPropertyCount();
+ for (int i = 0; i < count; i++)
+ {
+ if (shader.GetPropertyName(i) != propName) continue;
+
+ var type = shader.GetPropertyType(i);
+ if (type != ShaderPropertyType.Float && type != ShaderPropertyType.Range) return false;
+
+ value = shader.GetPropertyDefaultFloatValue(i);
+ return true;
+ }
+ return false;
+ }
+
public void UpdateObject(ref ObjectUpdateInfo updateInfo) {
if (updateInfo.data == null)