- Home /
Find materials that use a certain shader
As my project grows steadily in size, there are a lot of assets that are being used.
Working with so many assets, I have found that some are using the wrong shader (I found that out as some shaders were added to the build that should not be used).
I found it very time consuming tracking these rogue materials manually by checking each one for shader type. Is there a way to speed up the process? Like selecting a shader type and getting a list of all materials/objects that use this type?
Tnx a bunch
Hmm are you wanting to search materials of objects in your assets or in the hierarchy?
If your objects are in your hierarchy, you can simply do a quick loop through all of your objects, find the materials and the shader that is used by the material. Then just build an array of materials with the shader you are looking for. Here's a rough untested example in c#:
//find all of the objects either within the scene or from a created list of GameObjects, then run a loop for each found GameObject something like the following:
string shaderToLookFor = "diffuse"; //the shader you want to find
ArrayList<GameObject> objectsWithShader = ArrayList<GameObject>(); //the gameObjects found with the shader
foreach(GameObject go in foundGameObjects)
{
if(go.renderer.material.shader.name == shaderToLookFor)
{
objectsWithShader.Add(go);
}
}
I haven't tested this but it should work.
Your solution works well, very similar to robertbu's.
Tnx for your help
Answer by robertbu · Aug 09, 2013 at 04:24 AM
I did not see an easy way of finding all the material in the project, but it is straight forward to find all the materials that are in use in the project. So the following editor script will give a list of all the materials that are attached to any Renderer in a project that have the specified shader. Right now you must enter the full name. So it might be something convoluted like "Custom/unlit/old/Fringing With Transparency." You could change the comparison to a string.Contains() to do a substring match for something more forgiving. I've only lightly tested it.
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class FindShaderUse : EditorWindow {
string st = "";
string stArea = "Empty List";
[MenuItem("Window/Find Shader Use")]
public static void ShowWindow() {
EditorWindow.GetWindow(typeof(FindShaderUse));
}
public void OnGUI() {
GUILayout.Label("Enter shader to find:");
st = GUILayout.TextField (st);
if (GUILayout.Button("Find Materials")) {
FindShader(st);
}
GUILayout.Label(stArea);
}
private void FindShader(string shaderName) {
int count = 0;
stArea = "Materials using shader " + shaderName+":\n\n";
List<Material> armat = new List<Material>();
Renderer[] arrend = (Renderer[])Resources.FindObjectsOfTypeAll(typeof(Renderer));
foreach (Renderer rend in arrend) {
foreach (Material mat in rend.sharedMaterials) {
if (!armat.Contains (mat)) {
armat.Add (mat);
}
}
}
foreach (Material mat in armat) {
if (mat != null && mat.shader != null && mat.shader.name != null && mat.shader.name == shaderName) {
stArea += ">"+mat.name + "\n";
count++;
}
}
stArea += "\n"+count + " materials using shader " + shaderName + " found.";
}
}
Well i tested it for you and it works quite nice. It doesn't find all materials though - for example, it fails to find terrain materials (since it doesn't have a renderer) or materials that are assigned to a renderer only through code at runtime, but thats O$$anonymous$$ for me.
Taking your code I have expanded the solution a bit and added a toggle for exact name or string.Contains().
Tnx for your help!
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class FindShaderUse : EditorWindow
{
string st = "";
string stArea = "Empty List";
Vector2 scrollPos;
bool exact;
[$$anonymous$$enuItem("Window/Find Shader Use")]
public static void ShowWindow()
{
EditorWindow.GetWindow(typeof(FindShaderUse));
}
public void OnGUI()
{
GUILayout.Label("Enter shader to find:");
EditorGUILayout.BeginHorizontal();
st = GUILayout.TextField(st);
EditorGUIUtility.LookLikeControls(40);
exact = EditorGUILayout.Toggle("Exact", exact, GUILayout.Width(60));
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Find $$anonymous$$aterials"))
{
FindShader(st);
}
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(position.width), GUILayout.Height(position.height-60));
GUILayout.Label(stArea);
EditorGUILayout.EndScrollView();
}
private void FindShader(string shaderName)
{
int count = 0;
stArea = "$$anonymous$$aterials using shader " + shaderName + ":\n\n";
List<$$anonymous$$aterial> armat = new List<$$anonymous$$aterial>();
Renderer[] arrend = (Renderer[])Resources.FindObjectsOfTypeAll(typeof(Renderer));
foreach (Renderer rend in arrend)
{
foreach ($$anonymous$$aterial mat in rend.shared$$anonymous$$aterials)
{
if (!armat.Contains(mat))
{
armat.Add(mat);
}
}
}
foreach ($$anonymous$$aterial mat in armat)
{
if (mat != null && mat.shader != null && mat.shader.name != null)
{
if ((exact && mat.shader.name == shaderName) || (!exact && mat.shader.name.Contains(shaderName)))
{
stArea += ">" + mat.name + "\n";
count++;
}
}
}
stArea += "\n" + count + " materials using shader " + shaderName + " found.";
}
}
Answer by msklywenn · Oct 24, 2016 at 11:50 AM
This works for all assets, not just the loaded ones :
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class ShaderOccurenceWindow : EditorWindow
{
[MenuItem("Tools/Shader Occurence")]
public static void Open()
{
GetWindow<ShaderOccurenceWindow>();
}
Shader shader;
List<string> materials = new List<string>();
Vector2 scroll;
void OnGUI()
{
Shader prev = shader;
shader = EditorGUILayout.ObjectField(shader, typeof(Shader), false) as Shader;
if (shader != prev)
{
string shaderPath = AssetDatabase.GetAssetPath(shader);
string[] allMaterials = AssetDatabase.FindAssets("t:Material");
materials.Clear();
for (int i = 0; i < allMaterials.Length; i++)
{
allMaterials[i] = AssetDatabase.GUIDToAssetPath(allMaterials[i]);
string[] dep = AssetDatabase.GetDependencies(allMaterials[i]);
if (ArrayUtility.Contains(dep, shaderPath))
materials.Add(allMaterials[i]);
}
}
scroll = GUILayout.BeginScrollView(scroll);
{
for (int i = 0; i < materials.Count; i++)
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(Path.GetFileNameWithoutExtension(materials[i]));
GUILayout.FlexibleSpace();
if (GUILayout.Button("Show"))
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(materials[i], typeof(Material)));
}
GUILayout.EndHorizontal();
}
}
GUILayout.EndScrollView();
}
}
Doesn't work with inbuilt shaders but easy to fix, thanks!
@chrisvarnsverryvirtualarts True! Could you share your modified code?
Sure, my solution is to actually load the material and just compare the shader
using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; public class ShaderOccurenceWindow : EditorWindow { [$$anonymous$$enuItem("Tools/Shader Occurence")] public static void Open() { GetWindow<ShaderOccurenceWindow>(); } Shader shader; List<string> materials = new List<string>(); Vector2 scroll; void OnGUI() { Shader prev = shader; shader = EditorGUILayout.ObjectField(shader, typeof(Shader), false) as Shader; if (shader != prev) { string shaderPath = AssetDatabase.GetAssetPath(shader); string[] all$$anonymous$$aterials = AssetDatabase.FindAssets("t:$$anonymous$$aterial"); materials.Clear(); for (int i = 0; i < all$$anonymous$$aterials.Length; i++) { all$$anonymous$$aterials[i] = AssetDatabase.GUIDToAssetPath(all$$anonymous$$aterials[i]); var material = AssetDatabase.LoadAssetAtPath<$$anonymous$$aterial>(all$$anonymous$$aterials[i]); if(material.shader == shader) materials.Add(all$$anonymous$$aterials[i]); } } scroll = GUILayout.BeginScrollView(scroll); { for (int i = 0; i < materials.Count; i++) { GUILayout.BeginHorizontal(); { GUILayout.Label(Path.GetFileNameWithoutExtension(materials[i])); GUILayout.FlexibleSpace(); if (GUILayout.Button("Show")) EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(materials[i], typeof($$anonymous$$aterial))); } GUILayout.EndHorizontal(); } } GUILayout.EndScrollView(); } }
apologise for the formatting can't seem to get it to work.
Thank you for sharing! Some update with formating, small fixes and ability to change shaders of all found materials feature.
public class MaterialsUpdate : EditorWindow
{
[MenuItem("Tools/Shader Occurence and Update")]
public static void Open() { GetWindow<MaterialsUpdate>(); }
Shader shader;
Shader shaderChangeTo;
List<string> materials = new List<string>();
Vector2 scroll;
void UpdateScrollView()
{
// Find materials by shader type selected by user in field "Shader to find"
string shaderPath = AssetDatabase.GetAssetPath(shader);
string[] allMaterials = AssetDatabase.FindAssets("t:Material");
materials.Clear();
for (int i = 0; i < allMaterials.Length; i++)
{
allMaterials[i] = AssetDatabase.GUIDToAssetPath(allMaterials[i]);
var material = AssetDatabase.LoadAssetAtPath<Material>(allMaterials[i]);
if (material.shader == shader) materials.Add(allMaterials[i]);
}
// Put materials paths to scroll view
scroll = GUILayout.BeginScrollView(scroll);
{
for (int i = 0; i < materials.Count; i++)
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(Path.GetFileNameWithoutExtension(materials[i]));
GUILayout.FlexibleSpace();
if (GUILayout.Button("Show"))
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(materials[i], typeof(Material)));
}
GUILayout.EndHorizontal();
}
}
GUILayout.EndScrollView();
}
void OnGUI()
{
Shader prev = shader;
GUILayout.Label("Shader to find");
shader = EditorGUILayout.ObjectField(shader, typeof(Shader), false) as Shader;
GUILayout.Label("Shader change to");
shaderChangeTo = EditorGUILayout.ObjectField(shaderChangeTo, typeof(Shader), false) as Shader;
if (shader != prev)
UpdateScrollView();
// Shange material shader to selected by user in field "Shader change to"
if (GUILayout.Button("Change All"))
{
if (shaderChangeTo && shaderChangeTo != shader)
{
materials.ForEach(delegate (string material)
{
Material materialAsset = AssetDatabase.LoadAssetAtPath<Material>(material);
materialAsset.shader = shaderChangeTo;
});
UpdateScrollView();
}
else
{
Debug.Log("WARNING: MaterialsUpdate - No valid shader change to");
}
}
}
}