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

Rendering scene multiple times to image files from script

Discussion in 'Editor & General Support' started by ScottieBizzle, Jul 12, 2015.

  1. ScottieBizzle

    ScottieBizzle

    Joined:
    Nov 9, 2013
    Posts:
    17
    I have developed a programmable node-based asset authoring tool ( NodeFlex ) and I'm in the process of also developing an advanced shader graph system for it and Unity.

    http://www.shaderplay.com/products/nodeflex/overview/overview.html.

    I'm looking to support per-node previews and need to render out the scene ( using the current camera ) multiple times to an image file or possibly render the scene to a smaller render target multiple times ( all without updating to the viewport ). Ideally this would all happen in the AssetPreprocessor:OnPostprocessAllAssets callback script function when a NodeFlex shader is changed. I would then parse a line in the shader file telling me how many previews to render and I would also need to set one of my material's shader variables each time I render.

    If this is possible, any direction would be greatly appreciated. I'm new to Unity, but really excited to get this part working.
     
  2. ScottieBizzle

    ScottieBizzle

    Joined:
    Nov 9, 2013
    Posts:
    17
    Managed to figure it out and get it all working, but had to settle with the main camera instead of the viewport camera since it kept coming back null possibly since I'm calling this all in an AssetPostprocessor callback. Also couldn't get the shader name from the shader's asset pass which is all that OnPostprocessAllAssets gives you, so I just encoded it into the shader file along with my preview values which I parse. I use a directory change notification in NodeFlex to auto-load the preview snapshots as they change. I'm writting my own NodeFlex material inspector so i'll be able to fire off previews when the user plays with materials settings. The only thing it could use is a direct connection to NodeFlex so I could stream over dynamic values for the node ports.

    Here's a snapshot and the code I used.



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