Search Unity

Proper way to swap shaders back and forth?

Discussion in 'Scripting' started by Pysassin, Jul 8, 2014.

  1. Pysassin

    Pysassin

    Joined:
    Dec 27, 2012
    Posts:
    87
    I need to make a script that changes the shader on all of an objects (and its childrens) renderer to a certain shader, do some stuff, then return it to the original. I know you can procedurally so I don't have to make two prefabs/set of materials but I'm not quite sure what'd be the best way going about this. Anyone got any suggestions?
     
  2. WizardCube

    WizardCube

    Joined:
    Dec 31, 2013
    Posts:
    77
  3. Pysassin

    Pysassin

    Joined:
    Dec 27, 2012
    Posts:
    87
    Some of my models have 1 material others have 5 sorta thing. I know that's an option but I also know that doing so will make a double of the material meaning double the resources if I don't clear it when I switch back etc. Being that my game is a web build I'd like to keep resource use to a minimum.
     
  4. WizardCube

    WizardCube

    Joined:
    Dec 31, 2013
    Posts:
    77
    Oh, that's a tough one but I'll bump the thread at least haha
     
  5. Pysassin

    Pysassin

    Joined:
    Dec 27, 2012
    Posts:
    87
    Something I should maybe ask in shader section?
     
  6. TooManySugar

    TooManySugar

    Joined:
    Aug 2, 2015
    Posts:
    864
    Ok. This is the way I've solved the situation. The approach has 1 limitation, while it supports looking into multimaterials for specific shader types, it will not store multiple materials to swap within that multimaterial, as this is not my need, and as this would involve something like dictionary nesting which will for sure complicate the thing I've not done.

    My script allows destroyable objects so if you've then make sure it runs the Cleanup method, if not you could delete it.

    In my particular case the scene starts with some specific shaders applied for which I look for on start, it stores the renderers of this materials in a dictionary aswell as the original shader and mat in an struct in the value field.

    Otherwise if you whant the data to be stored before start you will need a serialiable dictionary and store the data into a file which I'm not in the need for. Not too difficult yet more unnecesary work.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. //struct for storing values in dictionary
    6. public struct SwapData{
    7.     public Material mat;
    8.     public Shader shad;
    9. }
    10.  
    11.  
    12. public class ShaderSwapper : MonoBehaviour {
    13.  
    14.     public static ShaderSwapper _SP;
    15.  
    16.     public Transform MeshesToSwap_root;//the root object where meshes tha will have shader swapped.
    17.     private Shader shader0;
    18.     private Shader shader1;
    19.     private Shader shader2;
    20.     private Shader shader3;
    21.  
    22.     SwapData dat;
    23.  
    24.     Dictionary <Renderer,SwapData> rendsDic = new Dictionary<Renderer,SwapData>();
    25.  
    26.     bool swap;
    27.  
    28.     void Start(){
    29. _SP=this;
    30.     dat = new SwapData();
    31.     //hd shaders
    32.     shader0 = Shader.Find("Reflective/Bumped Specular");
    33.     shader1 = Shader.Find("Reflective/Bumped Diffuse");
    34.     shader2 = Shader.Find("Bumped Diffuse");
    35.  
    36.  
    37.  
    38.     //SimpleShader
    39.     shader3 = Shader.Find("Diffuse");
    40.  
    41.     //Populate Dictionary (by default my scenes come with HD shader applied)
    42.     Renderer[] rends = MeshesToSwap_root.GetComponentsInChildren <Renderer> ();
    43.         foreach (Renderer r in rends) {
    44.             foreach (Material m in r.materials){//for multimaterials not skip materials with this shader with index higher than 0
    45.                 if (m.shader ==shader0 ||m.shader ==shader1 || m.shader ==shader2){
    46.                     if (!rendsDic.ContainsKey (r)){
    47.                             dat.mat =m;
    48.                             dat.shad =m.shader;
    49.                         rendsDic.Add (r,dat); // if not already added, add.
    50.                     }
    51.                 }
    52.             }
    53.         }
    54.  
    55.     }
    56.  
    57.     //test//test
    58.     void Update(){
    59.         if (Input.GetKey(KeyCode.O)){
    60.             swap =!swap;
    61.             Cleanup();
    62.             if (swap){        SwapToSDShader ();}
    63.             else{             SwapToHDShader();}
    64.         }
    65.     }
    66.  
    67.     //IF YOU HAVE DESTROYABLE OBJECTS you need to cleanup the dictionary, if not, you can skip the CLEANUP method.
    68.     void Cleanup(){
    69.         List<Renderer> null_keys = new List<Renderer>();
    70.         foreach (KeyValuePair<Renderer,SwapData> kp in rendsDic) {
    71.             if (kp.Key == null){
    72.                 null_keys.Add (kp.Key);
    73.             }
    74.         }
    75.  
    76.         foreach (Renderer r in null_keys) {
    77.             rendsDic.Remove (r);
    78.             print (r +" removed ///////");
    79.         }    
    80.     }
    81.  
    82.  
    83.     void SwapToSDShader (){
    84.         //I go trough each Dictionary entry and swap the shader
    85.         foreach (KeyValuePair<Renderer,SwapData> kp in rendsDic) {
    86.             foreach(Material m in kp.Key.materials){
    87.                 if (kp.Key.transform){//if object hast not been destroyed
    88.                     if (m.shader ==shader0 ||m.shader ==shader1 || m.shader ==shader2){    
    89.                         m.shader = shader3;
    90.                     }
    91.                 }
    92.             }
    93.         }
    94.     }
    95.  
    96.     void SwapToHDShader(){
    97.         //I go trough each Dictionary entry and restore original HD shader stored in dictionary
    98.         foreach (KeyValuePair<Renderer,SwapData> kp in rendsDic) {        
    99.             SwapData tempData= kp.Value;
    100.             foreach(Material m in kp.Key.materials){
    101.                 if (kp.Key.transform){//if object hast not been destroyed
    102.                     if(m == tempData.mat){ m.shader = tempData.shad;}
    103.                 }
    104.             }
    105.         }
    106.     }
    107. }
    108.  
    Tested under Unity 4.7.2
     
    Last edited: May 27, 2017