Skip to content

Instantly share code, notes, and snippets.

@rngtm
Last active March 13, 2024 04:46
Show Gist options
  • Save rngtm/ac93332c42e399c9b7bf5d8f51bad6ea to your computer and use it in GitHub Desktop.
Save rngtm/ac93332c42e399c9b7bf5d8f51bad6ea to your computer and use it in GitHub Desktop.
Unityプロジェクト内に存在するすべてのマテリアルのシェーダーキーワードをShaderVariantCollectionへ登録するツール (Unity2022 / URP14.0.10)
namespace MyTool
{
using System;
using UnityEditor;
using UnityEngine;
using System.Linq;
using UnityEngine.Rendering;
public class ShaderKeywordCollector : EditorWindow
{
[SerializeField] private ShaderVariantCollection shaderVariantCollection;
private Vector2 _scrollPosition;
private Material[] _materials = { };
private int _materialCount;
[MenuItem("Window/Shader Keyword Collector")]
public static void ShowWindow()
{
GetWindow<ShaderKeywordCollector>("Shader Keyword Collector");
}
void OnGUI()
{
GUILayout.Label("Shader Keyword Collector", EditorStyles.boldLabel);
// ShaderVariantCollection登録フィールド
shaderVariantCollection = EditorGUILayout.ObjectField("Shader Variant Collection", shaderVariantCollection,
typeof(ShaderVariantCollection), false) as ShaderVariantCollection;
// キーワード取得
EditorGUI.BeginDisabledGroup(shaderVariantCollection == null);
if (GUILayout.Button("Collect Keywords"))
{
Execute();
}
EditorGUI.EndDisabledGroup();
GUILayout.Space(16);
GUILayout.Label($"Collected Materials ({_materialCount})", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
DrawCollectedMaterial();
EditorGUI.indentLevel--;
EditorGUILayout.EndScrollView();
}
private void DrawCollectedMaterial()
{
if (_materials == null)
{
return;
}
foreach (var material in _materials)
{
if (material == null)
{
continue;
}
var shader = material.shader;
if (shader == null)
{
continue;
}
EditorGUILayout.ObjectField(material, typeof(Material));
}
}
private void Execute()
{
if (shaderVariantCollection == null)
{
Debug.LogError("Please assign a Shader Variant Collection.");
return;
}
shaderVariantCollection.Clear();
_materials = Resources.FindObjectsOfTypeAll<Material>()
.Where(IsCollectTarget)
.ToArray();
_materialCount = _materials.Length;
foreach (var material in _materials)
{
if (material.shader == null) continue;
AddMaterialVariant(shaderVariantCollection, material);
}
EditorUtility.SetDirty(shaderVariantCollection);
AssetDatabase.SaveAssets();
Debug.Log("Shader keywords collected and added to the Shader Variant Collection.");
}
/// <summary>
/// 登録対象のマテリアルか判別
/// </summary>
private static bool IsCollectTarget(Material material)
{
if (material == null)
{
return false;
}
if (material.hideFlags == HideFlags.DontSave)
{
return false;
}
var shader = material.shader;
if (shader == null)
{
return false;
}
if (shader.hideFlags == HideFlags.DontSave)
{
return false;
}
if (shader.name.StartsWith("Hidden/"))
{
return false;
}
return true;
}
/// <summary>
/// ShaderVariantCollectionへマテリアルのシェーダーキーワードを登録
/// </summary>
private static void AddMaterialVariant(ShaderVariantCollection collection, Material material)
{
Shader shader = material.shader;
if (material.shader == null) return;
int subShaderCount = shader.subshaderCount;
int passCount = shader.passCount;
for (uint subShaderIndex = 0; subShaderIndex < subShaderCount; subShaderIndex++)
{
for (uint passIndex = 0; passIndex < passCount; passIndex++)
{
AddPassVariant(collection, shader, passIndex, material.shaderKeywords);
}
}
}
/// <summary>
/// ShaderVariantCollectionへのVariant登録
/// </summary>
private static void AddPassVariant(ShaderVariantCollection collection, Shader shader, uint passIndex,
string[] keywords)
{
var variant = new ShaderVariantCollection.ShaderVariant
{
shader = shader,
passType = GetLightModePassType(shader, (int)passIndex),
keywords = keywords
};
collection.Add(variant);
}
/// <summary>
/// PassTypeの取得
/// </summary>
private static PassType GetLightModePassType(Shader shader, int passIndex)
{
// ShaderのLightModeパスタグの値を利用して、PassTypeを判別
// PassTypeについては https://docs.unity3d.com/ja/2019.4/ScriptReference/Rendering.PassType.html を参照
ShaderTagId lightModeTag = shader.FindPassTagValue(passIndex, new ShaderTagId("LightMode"));
bool success = Enum.TryParse<PassType>(lightModeTag.name, true, out PassType passType);
if (success)
{
return passType;
}
switch (lightModeTag.name)
{
case "SRPDefaultUnlit":
case "": // URPでは、LightModeパスタグが指定されていない場合は SRPDefaultUnlit として扱われる
return PassType.ScriptableRenderPipelineDefaultUnlit;
default:
return PassType.ScriptableRenderPipeline;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment