Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Swapping shaders in realtime possible?

Discussion in 'Scripting' started by DeanPunchard, Sep 20, 2012.

  1. DeanPunchard

    DeanPunchard

    Joined:
    Sep 20, 2012
    Posts:
    22
    Hey guys,

    I have just a quick question. I want to be able to change the shader on various objects in real-time. I have looked through the references, but can only see how to change the shader type and it's properties, which isn't what I want to do.

    Basically, it's so the user can click on the object, and change the shader. Some shaders are reflective, others diffuse, etc. I could do this by hiding / un-hiding duplicate geometry, but that seems a backward way of doing it.

    Thanks,

    Dean
     
  2. UnityCoder

    UnityCoder

    Joined:
    Dec 8, 2011
    Posts:
    534
    Just reffer to "renderer.material.shader" in Unity Documentation.

    There is one example for changing a shader at runtime.
     
  3. DeanPunchard

    DeanPunchard

    Joined:
    Sep 20, 2012
    Posts:
    22
    OK I see the page you are referring to and it says -

    Code (csharp):
    1.  
    2. // Create a material from code
    3.  
    4. function Start () {
    5.     // Create a material with transparent diffuse shader
    6.     var material = new Material (Shader.Find ("Transparent/Diffuse"));
    7.     material.color = Color.green;
    8.     // assign the material to the renderer
    9.     renderer.material = material;
    10. }
    11.  
    But that is creating a new material. I'm looking to use one already created, but perhaps not applied to any objects in the scene. Does that make sense?

    Thanks,

    Dean
     
  4. BPPHarv

    BPPHarv

    Joined:
    Jun 9, 2012
    Posts:
    318
    Using the example script you would move the material variable to the top to create a class global (what ever those are called) and then drag the material from your hierarchy into that value. You'll then have a reference to the material you've provided instead of creating another.

    Code (csharp):
    1.  
    2.     //reference to existing material set in the inspector / editor
    3.     var material : Material;    
    4.     function Start () {
    5.        //modify the source material reference.
    6.        material.color = Color.green;
    7.  
    8.        // assign the material to the renderer
    9.        renderer.material = material;
    10.     }
    11.  
    12.  
     
    Last edited: Sep 20, 2012
  5. UnityCoder

    UnityCoder

    Joined:
    Dec 8, 2011
    Posts:
    534
    No i m not talking about material. I m talking about shader, below is example for that:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class deletethis : MonoBehaviour {
    5.  
    6.     // Use this for initialization
    7.     void Start () {
    8.         renderer.material.shader=Shader.Find("Transparent/Diffuse");
    9.    
    10.     }
    11.    
    12. }
    13.  

    What i have done take a sphere, apply a material to that sphere with Reflective/Diffuse shader. After applying this script its change a material shader at runtime.
     
  6. BPPHarv

    BPPHarv

    Joined:
    Jun 9, 2012
    Posts:
    318
    When you link the material you also get a reference to it's shader. Now in my example I just set the value of the color.. in your case you would use the shader value used on that material instead.

    Code (csharp):
    1. public var materialval : Material;
    2.  
    3. function Start ()
    4. {
    5. renderer.material.shader = materialval.shader;
    6. }
    You end up with an instanced material using original name but with the updated shader.

    You could also make the global a direct shader reference and just swap it out. I can never seem to locate the stock shaders so I use material references for those and shader references for my own custom shaders.

    Code (csharp):
    1. public var shaderRef : Shader;
    2.  
    3. function Start ()
    4. {
    5. renderer.material.shader = shaderRef;
    6. }
    Both of these methods rely on the default values of the shaders. If you have specific values that you need to pass you'd set them in code which is why most people swap materials instead of shaders as those contain not only references to stuff but also data for the stuff to use.
     
    Last edited: Sep 20, 2012
  7. DeanPunchard

    DeanPunchard

    Joined:
    Sep 20, 2012
    Posts:
    22
    So BBPHarv and UnityCoder, there isn't a way to to swap shaders, which are already built in the Project Folders?

    Is there a simple way to copy a shader from one object to another? I'm thinking I could setup some objects with all shaders applied, then just copy them at real time to the objects I want to see.

    Thanks for your help so far.

    Dean
     
  8. BPPHarv

    BPPHarv

    Joined:
    Jun 9, 2012
    Posts:
    318
    Define "which are already built in the Project Folders?" are you talking about the stock shaders or your own custom ones or something else entirely?

    UnityCoders example provides a method to swap shaders at runtime by finding it by name.
    My examples provide methods to swap shaders from direct material and shader references.

    Both have the exact same effect in the end but have different workflows.

    The key here is that you need a shader reference from some place either linked from hierarchy or found using Shader.Find.
     
    Last edited: Sep 20, 2012
  9. DeanPunchard

    DeanPunchard

    Joined:
    Sep 20, 2012
    Posts:
    22
    Sorry, my knowledge of Unity is still pretty basic.

    Say I create 3 Materials in the Project Folder, "wood", "brick" and "metal". I then want to be able to apply these materials to objects in the scene / Hierarchy at real-time, and change them interactively.

    Maybe I'm getting confused, but the Shader.Find looks for the type of shader / material, not a name of a material?
     
  10. BPPHarv

    BPPHarv

    Joined:
    Jun 9, 2012
    Posts:
    318
    This is essentially correct.. Shader.Find finds a named Shader has nothing (not much) to do with materials.

    Using the references to the materials themselves would allow you to make cubes (any gameobject but cubes are simple to grasp) wood,brick or metal. Changing the shader on a game object's material would make a solid wood cube a transparent wood cube (for instance).

    Either way you end up with an array of materials or specific hard coded variables to hold the references to your base materials (wood,metal,brick).

    Assuming you want to turn cubes into wood,brick or metal you could then do.
    Code (csharp):
    1.  
    2. public var woodMat:Material;
    3. public var brickMat:Material;
    4. public var metalMat:Material;
    5.  
    6. function ChangeObjectAppearance(objectToChange : GameObject , newmaterial:string)
    7. {
    8. if(newmaterial=="wood")
    9.    {
    10.    objectToChange .renderer.material=woodMat;//game object is now wood
    11.    }
    12. if(newmaterial=="brick")
    13.    {
    14.    objectToChange .renderer.material=brickMat;//game object is now brick
    15.    }
    16. if(newmaterial=="metal")
    17.    {
    18.    objectToChange .renderer.material=metalMat;//game object is now metal
    19.    }
    20. }
    and in your interactive code you would call ChangeObjectAppearance(cubeobject,"wood");

    OR using an array

    Code (csharp):
    1.  
    2. public var mySourceMaterials : Material[];
    3.  
    4. function ChangeObjectAppearance(objectToChange : GameObject , newmaterial:int)
    5. {
    6. gameObject.renderer.material=mySourceMaterials [newmaterial]; //game object is which ever material you linked and specified
    7. }
    8.  
    and in your interactive code you would call ChangeObjectAppearance(cubeobject,0); assuming wood was the first material you dragged into the array in the inspector.

    Either way you would manually drag the 3 materials in the inspector onto the relevant coded material.
    In the hard coded case you would drag the wood material onto the entry called woodMat, brick onto the brickMat and metal onto the metalMat.

    In the array case you would manually drag the 3 materials in the inspector onto the mySourceMaterials entry in the inspector. Each time you drop a a material a new entry will be added.

    Swapping out the shader directly would be more of using the shader to indicate that the player is hovering over it by turning it transparent or glowy or what ever.

    In this case your materials wouldn't be wood,brick metal they would be ViewNormal and VewTransparent. So when you moved the mouse you would call a modified version of the array based ChangeObjectAppearance function that looked similar to this
    Code (csharp):
    1.  
    2. gameObject.renderer.material.shader=mySourceMaterials[newmaterial].shader;
    3.  
    where the mySourceMaterials array held references to your ViewNormal and ViewTransparent materials.
     
    Last edited: Sep 20, 2012
  11. DeanPunchard

    DeanPunchard

    Joined:
    Sep 20, 2012
    Posts:
    22
    Hi BPPHarv

    I think I was probably over complicating a simple issue, and I needed to take things back to basics in order to get something to work for a simple demo. I looked through your code and ideas, and although I didn't fully understand it, was able to follow your concept and work from there. I hadn't considered using Arrays before, and I didn't even know you could drag and drop materials into a script!

    So anyway, below is something really simple, but something that might be worth sharing. The script is applied to any object, then the materials added to the array in the inspector. I also had to delete the material from the 3D object, so it didn't have any material in at all.

    I'm sorry if my initial post wasn't clear, but thank you for your time, and I will look to implement it further when the project proceeds.

    Code (csharp):
    1.  
    2. var materials : Material[];
    3. var count ="0";
    4.  
    5. function Update () {
    6.  
    7. if (count =="0"){
    8. renderer.material = materials[0];}
    9. if (count =="1"){
    10. renderer.material = materials[1];}
    11. if (count =="2"){
    12. renderer.material = materials[2];}
    13. if (count =="3"){
    14. renderer.material = materials[3];}
    15.  
    16. }
    17.  
    18.  
    19.  
    20.  
    21. function OnMouseDown () {
    22.  
    23. if (count=="0"){
    24. count = "1";
    25. }
    26. else if (count=="1"){
    27. count = "2";
    28. }
    29. else if (count=="2"){
    30. count = "3";
    31. }
    32. else if (count=="3"){
    33. count = "0";
    34. }
    35.  
    36.  
    37. }
    38.  
     
  12. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You could make that a ton shorter and skip the Update completely.

    Code (csharp):
    1.  
    2. var materials : Material[];
    3. var count = 0;
    4.  
    5. function OnMouseDown() {
    6.     if (count == materials.Length - 1)
    7.         count = 0;
    8.     else
    9.         count++;
    10.  
    11.     renderer.material = materials[count];
    12. }
    13.