123 lines
4.8 KiB
C#
123 lines
4.8 KiB
C#
using System.Collections.Generic;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// VRM 런타임 로딩에 필요한 셰이더를 빌드에 강제로 포함시킨다.
|
|
///
|
|
/// 왜 필요한가: Unity 는 어떤 머티리얼도 참조하지 않는 셰이더를 빌드에서 제거한다.
|
|
/// 에디터에서는 프로젝트의 모든 셰이더를 찾을 수 있어 문제가 드러나지 않지만,
|
|
/// 빌드된 플레이어에서는 Shader.Find 가 null 을 돌려주고
|
|
/// "ArgumentNullException: Parameter name: Shader" 로 로드가 실패한다.
|
|
///
|
|
/// 일반적인 게임이라면 씬에 쓰인 머티리얼을 보고 Unity 가 알아서 판단하지만,
|
|
/// 우리는 실행 중에 임의의 VRM 을 불러오므로 어떤 셰이더가 필요할지 빌드 시점에
|
|
/// 알 수 없다. 그래서 Always Included Shaders 에 명시적으로 등록한다.
|
|
/// </summary>
|
|
public static class VrmShaderBuildSetup
|
|
{
|
|
// UrpVrm10MaterialDescriptorGenerator / BuiltInVrm10MaterialDescriptorGenerator 가
|
|
// 참조하는 셰이더들. 현재 URP 를 쓰지만 파이프라인을 바꿔도 되도록 둘 다 넣는다.
|
|
static readonly string[] RequiredShaders =
|
|
{
|
|
"VRM10/Universal Render Pipeline/MToon10", // URP 용 MToon (지금 쓰는 것)
|
|
"VRM10/MToon10", // Built-in 용 MToon
|
|
"Universal Render Pipeline/Lit", // MToon 이 아닌 PBR 머티리얼
|
|
"Universal Render Pipeline/Unlit",
|
|
"UniGLTF/UniUnlit",
|
|
};
|
|
|
|
[MenuItem("Tools/Desktop Overlay/6. Include VRM Shaders In Build")]
|
|
public static void IncludeShaders()
|
|
{
|
|
var graphicsSettings = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/GraphicsSettings.asset");
|
|
if (graphicsSettings == null || graphicsSettings.Length == 0)
|
|
{
|
|
Debug.LogError("[VrmShaderBuildSetup] GraphicsSettings.asset 을 열지 못했습니다.");
|
|
return;
|
|
}
|
|
|
|
var so = new SerializedObject(graphicsSettings[0]);
|
|
var list = so.FindProperty("m_AlwaysIncludedShaders");
|
|
if (list == null)
|
|
{
|
|
Debug.LogError("[VrmShaderBuildSetup] m_AlwaysIncludedShaders 프로퍼티를 찾지 못했습니다.");
|
|
return;
|
|
}
|
|
|
|
// 이미 들어있는 것 수집
|
|
var existing = new HashSet<Shader>();
|
|
for (int i = 0; i < list.arraySize; i++)
|
|
{
|
|
var s = list.GetArrayElementAtIndex(i).objectReferenceValue as Shader;
|
|
if (s != null) existing.Add(s);
|
|
}
|
|
|
|
var added = new List<string>();
|
|
var already = new List<string>();
|
|
var notFound = new List<string>();
|
|
|
|
foreach (var name in RequiredShaders)
|
|
{
|
|
var shader = Shader.Find(name);
|
|
if (shader == null)
|
|
{
|
|
notFound.Add(name);
|
|
continue;
|
|
}
|
|
|
|
if (existing.Contains(shader))
|
|
{
|
|
already.Add(name);
|
|
continue;
|
|
}
|
|
|
|
list.InsertArrayElementAtIndex(list.arraySize);
|
|
list.GetArrayElementAtIndex(list.arraySize - 1).objectReferenceValue = shader;
|
|
existing.Add(shader);
|
|
added.Add(name);
|
|
}
|
|
|
|
so.ApplyModifiedProperties();
|
|
AssetDatabase.SaveAssets();
|
|
|
|
var sb = new StringBuilder("[VrmShaderBuildSetup] Always Included Shaders 갱신\n");
|
|
if (added.Count > 0)
|
|
{
|
|
sb.AppendLine($"추가됨 ({added.Count})");
|
|
foreach (var s in added) sb.AppendLine(" + " + s);
|
|
}
|
|
if (already.Count > 0)
|
|
{
|
|
sb.AppendLine($"이미 있음 ({already.Count})");
|
|
foreach (var s in already) sb.AppendLine(" = " + s);
|
|
}
|
|
if (notFound.Count > 0)
|
|
{
|
|
sb.AppendLine($"찾지 못함 ({notFound.Count}) — 해당 파이프라인을 안 쓰면 정상입니다");
|
|
foreach (var s in notFound) sb.AppendLine(" ? " + s);
|
|
}
|
|
sb.AppendLine();
|
|
sb.AppendLine("이제 다시 빌드하세요.");
|
|
|
|
Debug.Log(sb.ToString());
|
|
}
|
|
|
|
[MenuItem("Tools/Desktop Overlay/7. Log Always Included Shaders")]
|
|
public static void LogIncluded()
|
|
{
|
|
var graphicsSettings = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/GraphicsSettings.asset");
|
|
var so = new SerializedObject(graphicsSettings[0]);
|
|
var list = so.FindProperty("m_AlwaysIncludedShaders");
|
|
|
|
var sb = new StringBuilder($"[VrmShaderBuildSetup] Always Included Shaders ({list.arraySize}개)\n");
|
|
for (int i = 0; i < list.arraySize; i++)
|
|
{
|
|
var s = list.GetArrayElementAtIndex(i).objectReferenceValue as Shader;
|
|
sb.AppendLine($" [{i}] {(s != null ? s.name : "<null>")}");
|
|
}
|
|
Debug.Log(sb.ToString());
|
|
}
|
|
}
|