Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

(bug?) Shader not picking up old material values in AssetPostprocessor::OnPostprocessAllAssets

Discussion in 'General Graphics' started by ScottieBizzle, Jul 14, 2015.

  1. ScottieBizzle

    ScottieBizzle

    Joined:
    Nov 9, 2013
    Posts:
    17
    I'm using an update loop to Refresh the AssetDatabase and use AssetPostprocessor::OnPostprocessAllAssets to know when my external shaders were imported so that I can render out a bunch of node previews for my node-based asset authoring tool ( NodeFlex ). This all works for the most part, but it seems the material doesn't yet have of its old ( lingering ) values/textures set to it yet so the renders turn out black in some cases. The viewport gets updated however and _does_ have the material's values reset to them and renders just fine.

    For example, if my shader had an exposed texture named MyTexture and was set to use "sometexture.jpg", if I then remove that input in my shader code and recompile, the texture UI is removed from the inspector of course, but the old "sometexture.jpg" value is still retained in the material and is used the next time a texture input named MyTexture gets added back to the shader which is great. The problem is, in OnPostprocessAllAssets, this hasn't been done yet, those old values like "sometexture.jpg" aren't yet set so rendering with them immediately turns out wrong.

    I'm guessing this remapping of values happens after the asset post process callback, so is this a bug, or is there a way to tell a material to setup its shader values in script. I tried forcing the viewport to render and making other calls hoping it would just "fix up" these values so I could then render, but doesn't seem to work. And it's definitely the shader inputs not getting their old material values right away since it all renders fine in the viewport and if I touch the shader file to have it reload again, it works since the materials were fixed up by the editor the last time and are ready in the second call to OnPostprocessAllAssets

    Here is the C# code, hope I'm explaining this clearly.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.Text;
    6. using System.IO;
    7. //-------------------------------------------------------------------------------
    8. [InitializeOnLoad]
    9. class BackgroundRefresh : AssetPostprocessor
    10. {
    11.     const float             m_RefreshInterval = 0.1f; // in seconds
    12.     static double           m_NextRefreshTime;
    13.     static RenderTexture    m_TargetLarge;
    14.     static Texture2D        m_TextureLarge;
    15.     static RenderTexture    m_TargetSmall;
    16.     static Texture2D        m_TextureSmall;
    17.     static bool             m_TargetsInitialized = false;
    18.     //-------------------------------------------------------------------------------
    19.     static BackgroundRefresh()
    20.     {
    21.         EditorApplication.update += Update;
    22.     }
    23.     //-------------------------------------------------------------------------------
    24.     static void Update()
    25.     {
    26.         double time = EditorApplication.timeSinceStartup;
    27.      
    28.         if ( time > m_NextRefreshTime )
    29.         {
    30.             AssetDatabase.Refresh();
    31.             m_NextRefreshTime = time + m_RefreshInterval;
    32.         }
    33.     }
    34.     //-------------------------------------------------------------------------------
    35.     static void AddDebugInfo( string Text )
    36.     {
    37.         Debug.Log( Text + "\n" ); // removes the "UnityEngine.Debug:Log(Object)" text in the debug console
    38.     }
    39.     //-------------------------------------------------------------------------------
    40.     static public void RenderPreview( string Path, string Name, bool Full )
    41.     {
    42.         if ( !m_TargetsInitialized )
    43.         {
    44.             m_TargetLarge = new RenderTexture( 1280, 1280, 16 );
    45.             m_TextureLarge = new Texture2D( 1280, 1280, TextureFormat.RGB24, false );
    46.             m_TargetSmall = new RenderTexture( 256, 256, 16 );
    47.             m_TextureSmall = new Texture2D( 256, 256, TextureFormat.RGB24, false );
    48.             m_TargetsInitialized = true;
    49.         }
    50.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    51.  
    52.         if ( m_TargetLarge == null || m_TextureLarge == null || m_TargetSmall == null || m_TextureSmall == null )
    53.             return;
    54.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    55.  
    56.         Camera previewCamera;
    57.         if ( false ) // try to get the viewport camera
    58.         {
    59.             //EditorWindow.FocusWindowIfItsOpen<SceneView>();
    60.      
    61.             // GameObject OVcamera = GameObject.FindGameObjectWithTag("OverviewCamera");
    62.           //   yield return new WaitForEndOfFrame();
    63.                //  Camera camOV = Camera.current;;//OVcamera.GetComponent<Camera>();
    64.      
    65.             //if ( Camera.current != null )
    66.             //    AddDebugInfo( "YEP" );
    67.            // else
    68.            //     AddDebugInfo( "NOPE" );
    69.       //      Camera camOV = Camera.current;//new Camera();
    70.            // camOV.CopyFrom( Camera.current );
    71.             //Camera camOV = Camera.current;// OVcamera.GetComponent<Camera>();
    72.         }
    73.         else
    74.         {
    75.             GameObject OVcamera = GameObject.FindGameObjectWithTag( "MainCamera" );
    76.             if ( OVcamera == null )
    77.                 return;
    78.             previewCamera =  OVcamera.GetComponent<Camera>();
    79.             if ( previewCamera ==  null )
    80.                 return;
    81.         }
    82.         RenderTexture lastTarget = RenderTexture.active;
    83.         RenderTexture previewTarget  = ( Full ) ? m_TargetLarge : m_TargetSmall;
    84.         Texture2D     previewTexture = ( Full ) ? m_TextureLarge : m_TextureSmall;
    85.         previewCamera.targetTexture = previewTarget;
    86.         previewCamera.Render();
    87.         RenderTexture.active = previewTarget;
    88.         previewTexture.ReadPixels( new Rect( 0, 0, previewTarget.width, previewTarget.height ), 0, 0 );
    89.         previewTexture.Apply();
    90.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    91.  
    92.         // Reset
    93.         previewCamera.targetTexture = null;
    94.         RenderTexture.active = lastTarget; // added to avoid errors
    95.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    96.  
    97.         byte[] bytes = previewTexture.EncodeToPNG();
    98.         System.IO.File.WriteAllBytes( Path + Name, bytes );
    99.     }
    100.     //-------------------------------------------------------------------------------
    101.     static void RenderPreview( Shader UpdatedShader, string sNodeID, string sPassID )
    102.     {
    103.         //AddDebugInfo( "Preview: Node(" + sNodeID + ") - Pass (" + sPassID + ")" );
    104.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    105.  
    106.         int NodeID = int.Parse( sNodeID );
    107.         int PassID = int.Parse( sPassID );
    108.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    109.  
    110.         Material[] materialArray = (Material[])Resources.FindObjectsOfTypeAll(typeof(Material) );
    111.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    112.  
    113.         foreach ( Material material in materialArray )
    114.         {
    115.             if ( material.shader == UpdatedShader )
    116.             {
    117.                 material.SetInt( "g_PreviewNodeID", NodeID );
    118.                 material.SetInt( "g_PreviewPassID", PassID );
    119.             }
    120.         }
    121.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    122.  
    123.         RenderPreview( "c:/Snapshots/", "preview_n" + sNodeID + "_p" + sPassID + ".png", false );
    124.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    125.  
    126.         // Reset the material preview IDs
    127.         foreach ( Material material in materialArray )
    128.         {
    129.             if ( material.shader == UpdatedShader )
    130.             {
    131.                 material.SetInt( "g_PreviewNodeID", -1 );
    132.                 material.SetInt( "g_PreviewPassID", -1 );
    133.             }
    134.         }
    135.     }
    136.     //-------------------------------------------------------------------------------
    137.     static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths )
    138.     {
    139.         UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
    140.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    141.  
    142.         bool NeedsRender = false;
    143.         foreach ( var AssetPath in importedAssets )
    144.         {
    145.             if ( AssetPath.Contains( ".shader" ) )
    146.             {
    147.                 StreamReader FileReader = new StreamReader( AssetPath, Encoding.Default );
    148.                 string Line1 = FileReader.ReadLine();
    149.                 string Line2 = FileReader.ReadLine();
    150.                 FileReader.Close();
    151.                 if ( Line1 != null && Line2 != null )
    152.                 {
    153.                     Line2 = Line2.Substring( 3 );
    154.                     Line2.Trim();
    155.                  
    156.                     Shader UpdatedShader = Shader.Find( Line2 );
    157.                     if ( UpdatedShader != null )
    158.                     {
    159.                         string[] PreviewInfo = Line1.Split( ' ' );
    160.                         if ( PreviewInfo.Length > 2 && PreviewInfo[1] == "NODEFLEX" )
    161.                         {
    162.                             for ( var n = 2; n < PreviewInfo.Length; n++ )
    163.                             {
    164.                                 string[] PreviewIDs = PreviewInfo[n].Split( ':' );
    165.                                 if ( PreviewIDs.Length == 2 )
    166.                                 {
    167.                                     NeedsRender = true;
    168.                                     RenderPreview( UpdatedShader, PreviewIDs[0], PreviewIDs[1] );
    169.                                 }
    170.                             }
    171.                         }
    172.                     }
    173.                 }
    174.             }
    175.         }
    176.         // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    177.  
    178.         if ( NeedsRender )
    179.             RenderPreview( "c:/Snapshots/", "preview.png", true );
    180.     }
    181. }
     
  2. ScottieBizzle

    ScottieBizzle

    Joined:
    Nov 9, 2013
    Posts:
    17
    Found a workaround, postponed the rendering of my previews until after the OnPostprocessAllAssets was finished by saving the assets paths into a global variable and letting my Update callback handle it next time around.