Swapping shaders in realtime possible?

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

Just reffer to “renderer.material.shader” in Unity Documentation.

There is one example for changing a shader at runtime.

OK I see the page you are referring to and it says -

// Create a material from code

function Start () {
    // Create a material with transparent diffuse shader
    var material = new Material (Shader.Find ("Transparent/Diffuse"));
    material.color = Color.green;
    // assign the material to the renderer
    renderer.material = material;
}

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

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.

    //reference to existing material set in the inspector / editor
    var material : Material;     
    function Start () {
       //modify the source material reference.
       material.color = Color.green;

       // assign the material to the renderer
       renderer.material = material;
    }

No i m not talking about material. I m talking about shader, below is example for that:

using UnityEngine;
using System.Collections;

public class deletethis : MonoBehaviour {

	// Use this for initialization
	void Start () {
		renderer.material.shader=Shader.Find("Transparent/Diffuse");
	
	}
	
}

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.

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.

public var materialval : Material;

function Start () 
{
renderer.material.shader = materialval.shader;
}

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.

public var shaderRef : Shader;

function Start () 
{
renderer.material.shader = shaderRef;
}

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.

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

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.

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?

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.

public var woodMat:Material;
public var brickMat:Material;
public var metalMat:Material;

function ChangeObjectAppearance(objectToChange : GameObject , newmaterial:string)
{
if(newmaterial=="wood")
   {
   objectToChange .renderer.material=woodMat;//game object is now wood
   }
if(newmaterial=="brick")
   {
   objectToChange .renderer.material=brickMat;//game object is now brick
   }
if(newmaterial=="metal")
   {
   objectToChange .renderer.material=metalMat;//game object is now metal
   }
}

and in your interactive code you would call ChangeObjectAppearance(cubeobject,“wood”);

OR using an array

public var mySourceMaterials : Material[];

function ChangeObjectAppearance(objectToChange : GameObject , newmaterial:int)
{
gameObject.renderer.material=mySourceMaterials [newmaterial]; //game object is which ever material you linked and specified
}

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

gameObject.renderer.material.shader=mySourceMaterials[newmaterial].shader;

where the mySourceMaterials array held references to your ViewNormal and ViewTransparent materials.

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.

var materials : Material[];
var count ="0";

function Update () {

if (count =="0"){
renderer.material = materials[0];}
if (count =="1"){
renderer.material = materials[1];}
if (count =="2"){
renderer.material = materials[2];}
if (count =="3"){
renderer.material = materials[3];}

}




function OnMouseDown () {

if (count=="0"){
count = "1";
}
else if (count=="1"){
count = "2";
}
else if (count=="2"){
count = "3";
}
else if (count=="3"){
count = "0";
}


}

You could make that a ton shorter and skip the Update completely.

var materials : Material[];
var count = 0;

function OnMouseDown() {
    if (count == materials.Length - 1)
        count = 0;
    else 
        count++;

    renderer.material = materials[count];
}