Search Unity

Question How to get shader source code from script?

Discussion in 'Shader Graph' started by Misha_Kh, Apr 30, 2021.

  1. Misha_Kh

    Misha_Kh

    Joined:
    Apr 26, 2021
    Posts:
    5
    The main issue I trying to solve is that shader graphs are not working with unity's UI Mask (not maskable). This can be easily fixed by adding a few lines of code into generated shader code. What I try to do, is to automate the process: select shader graph asset in project -> choose menu button -> source code extracted from shader, lines added, new *.shader file created with modified code.
    So, that's the problem: how to get compiled code from script?

    I found out how to get particular Pass code (HLSL Program), but what about ShaderLab part?

    What I can do, is to grab generated code from Temp folder, or copy it from buffer. But for this, I need to press "View Generated Shader" or "Copy Shader"...
     
    UbiBenKenobi likes this.
  2. JohnOknelorfDev

    JohnOknelorfDev

    Joined:
    Oct 2, 2017
    Posts:
    21
    +1

    I would be happy to have this functionality at hand.
     
    Misha_Kh likes this.
  3. Misha_Kh

    Misha_Kh

    Joined:
    Apr 26, 2021
    Posts:
    5
    Personally I ended up with the next flow: choose shaderGraph file, press "Copy Shader" button, then go to my custom menu and launch my custom function - which will grab shader's code from clipboard, parse and modify it, and then creates a new shader file, and imports in via assets database. I'll ask the permission to share the code from my customer, let's see, what he'll answers.
     
  4. patrickb2022

    patrickb2022

    Joined:
    Oct 15, 2022
    Posts:
    2
    Hi, could anybody automate this?
     
  5. leohilbert

    leohilbert

    Joined:
    Nov 28, 2015
    Posts:
    21
    Until there is an official API you can use this (2021.3.18f1):
    Code (CSharp):
    1. private static string GenerateShaderCode(string shaderAssetPath, string shaderName = null)
    2. {
    3.     Type editorType =
    4.         Type.GetType("UnityEditor.ShaderGraph.ShaderGraphImporterEditor, Unity.ShaderGraph.Editor")!;
    5.     Type generatorType =
    6.         Type.GetType("UnityEditor.ShaderGraph.Generator, Unity.ShaderGraph.Editor")!;
    7.     Type modeType =
    8.         Type.GetType("UnityEditor.ShaderGraph.GenerationMode, Unity.ShaderGraph.Editor")!;
    9.  
    10.     MethodInfo method = editorType.GetMethod(
    11.         "<OnInspectorGUI>g__GetGraphData|2_0",
    12.         BindingFlags.NonPublic | BindingFlags.Static
    13.     )!;
    14.     shaderName ??= Path.GetFileNameWithoutExtension(shaderAssetPath);
    15.     var importer = AssetImporter.GetAtPath(shaderAssetPath);
    16.     object graphData = method.Invoke(null, new object[] { importer });
    17.  
    18.     // new Generator(graphData, null, GenerationMode.ForReals, assetName, null, true);
    19.     object forReals = ((FieldInfo)modeType.GetMember("ForReals")[0]).GetValue(null);
    20.     object generator = Activator.CreateInstance(
    21.         generatorType, graphData, null,
    22.         forReals, shaderName, null, true
    23.     );
    24.     object shaderCode = generatorType
    25.         .GetProperty("generatedShader", BindingFlags.Public | BindingFlags.Instance)!
    26.         .GetValue(generator);
    27.  
    28.     return (string)shaderCode;
    29. }
    It's hideous, but it works :D
     
    Last edited: May 26, 2023
  6. Farl_Lee

    Farl_Lee

    Joined:
    Mar 20, 2014
    Posts:
    5
    Some how I can't call the method by "<OnInspectorGUI>g__GetGraphData|2_0"
    So I implement a more complex one and support ShaderGraph 14.0
    Code (CSharp):
    1.     private static object GetGraphData(string shaderAssetPath)
    2.     {
    3.         var importer = AssetImporter.GetAtPath(shaderAssetPath);
    4.  
    5.         var textGraph = File.ReadAllText(importer.assetPath, Encoding.UTF8);
    6.         var graphObjectType = Type.GetType("UnityEditor.Graphing.GraphObject, Unity.ShaderGraph.Editor")!;
    7.  
    8.         // var graphObject = CreateInstance<GraphObject>();
    9.         var graphObject = ScriptableObject.CreateInstance(graphObjectType);
    10.  
    11.         graphObject.hideFlags = HideFlags.HideAndDontSave;
    12.         bool isSubGraph;
    13.         var extension = Path.GetExtension(importer.assetPath).Replace(".", "");
    14.         switch (extension)
    15.         {
    16.             case "shadergraph":
    17.                 isSubGraph = false;
    18.                 break;
    19.             case "ShaderGraph":
    20.                 isSubGraph = false;
    21.                 break;
    22.             case "shadersubgraph":
    23.                 isSubGraph = true;
    24.                 break;
    25.             default:
    26.                 throw new Exception($"Invalid file extension {extension}");
    27.         }
    28.         var assetGuid = AssetDatabase.AssetPathToGUID(importer.assetPath);
    29.        
    30.         // graphObject.graph = new GraphData { assetGuid = assetGuid, isSubGraph = isSubGraph, messageManager = null };
    31.         var graphObject_graphProperty = graphObjectType.GetProperty("graph")!;
    32.         var graphDataType = Type.GetType("UnityEditor.ShaderGraph.GraphData, Unity.ShaderGraph.Editor")!;
    33.         var graphDataInstance = Activator.CreateInstance(graphDataType);
    34.         graphDataType.GetProperty("assetGuid")!.SetValue(graphDataInstance, assetGuid);
    35.         graphDataType.GetProperty("isSubGraph")!.SetValue(graphDataInstance, isSubGraph);
    36.         graphDataType.GetProperty("messageManager")!.SetValue(graphDataInstance, null);
    37.         graphObject_graphProperty.SetValue(graphObject, graphDataInstance);
    38.  
    39.         // MultiJson.Deserialize(graphObject.graph, textGraph);
    40.         // = MultiJson.Deserialize<JsonObject>(graphObject.graph, textGraph, null, false);
    41.         var multiJsonType = Type.GetType("UnityEditor.ShaderGraph.Serialization.MultiJson, Unity.ShaderGraph.Editor")!;
    42.         var deserializeMethod = multiJsonType.GetMethod("Deserialize")!;
    43.         var descrializeGenericMethod = deserializeMethod.MakeGenericMethod(graphDataType);
    44.         descrializeGenericMethod.Invoke(null, new object[] { graphDataInstance, textGraph, null, false });
    45.  
    46.         // graphObject.graph.OnEnable();
    47.         graphDataType.GetMethod("OnEnable")!.Invoke(graphDataInstance, null);
    48.  
    49.         // graphObject.graph.ValidateGraph();
    50.         graphDataType.GetMethod("ValidateGraph")!.Invoke(graphDataInstance, null);
    51.  
    52.         // return graphData.graph
    53.         return graphDataInstance;
    54.     }
    55.  
    56.     private static string GenerateShaderCode(string shaderAssetPath, string shaderName = null)
    57.     {
    58.         Type generatorType =
    59.             Type.GetType("UnityEditor.ShaderGraph.Generator, Unity.ShaderGraph.Editor")!;
    60.         Type modeType =
    61.             Type.GetType("UnityEditor.ShaderGraph.GenerationMode, Unity.ShaderGraph.Editor")!;
    62.  
    63.         shaderName ??= Path.GetFileNameWithoutExtension(shaderAssetPath);
    64.  
    65.         object graphData = GetGraphData(shaderAssetPath);
    66.  
    67.         // new Generator(graphData, null, GenerationMode.ForReals, assetName, target:null, assetCollection:null, humanReadable: true);
    68.         object forReals = ((FieldInfo)modeType.GetMember("ForReals")[0]).GetValue(null);
    69.         object generator = Activator.CreateInstance(
    70.             generatorType,
    71.             new object[] { graphData, null, forReals, shaderName, null, null, true }
    72.         );
    73.         object shaderCode = generatorType
    74.             .GetProperty("generatedShader", BindingFlags.Public | BindingFlags.Instance)!
    75.             .GetValue(generator);
    76.  
    77.         return (string)shaderCode;
    78.     }