Having trouble getting a material.SetFloat() to work on custom shader I wrote.

I have a simple unlit dissolve shader here and i have a float value called _Level that controls the Dissolution level for the dissolve. It works great in editor but i also want to be able to control it in script using DoTween DoFloat() which doesn’t work. Not only that but I’m not even able to get unity engine’s material.SetFloat() to work either for some reason.

Here are the parts of the code that are important for this:

Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _MainColor("Main Color", Color) = (1,1,1,1)

        _NoiseTex("Texture", 2D) = "white" {}
        _Dissolution("Dissolution level", float) = 0
    }

then i also have the _Level float property set to “uniform” within the SubShader:

uniform float _Dissolution;

in C# i am calling this:

        _mat = _meshRenderer.material;
        _meshRenderer.material = dissolveMaterial;
        Shader.SetGlobalFloat("_Dissolution", .5f);

I have also tried mat.SetFloat() but with no success. What am i missing here?
thanks for any help with this! :slight_smile:

You’re using the global property setting instead of local. You would have to use dissolveMaterial.SetFloat("_Dissolution", .5f); so that you’re setting the _Dissolation value on that instance of the material.

If you want to be able to have it be a global value shared by all materials with this shader, and thus be able to use SetGlobalFloat, then you’d want to remove it from the Properties{} section of your shader.

Oh my bad I made a typo at the bottom, I meant i have used SetFloat() aswell and it didnt work.

so i tried both:

dissolveMaterial.SetFloat("_Dissolution", .5f);

Shader.SetGlobalFloat("_Dissolution", .5f);

with no success :c

I don’t really want it to be global and it would be nice to keep the property to have the freedom to change it in inspector if need be. So mat.SetFloat() sounds ideal which was actually the first thing I tried.

Could we see more of your code that’s executing during this? Because it should work fine if the only thing you’re doing is assigning the material and doing thatMaterial.SetFloat().

sure thing. here is the entire dissolve shader. I’ve added a little extra since my last reply. but still same issues:

Shader "Unlit/DissolveShader"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _MainColor("Main Color", Color) = (1,1,1,1)

        _NoiseTex("Texture", 2D) = "white" {}
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
        _EdgeColor1("Edge colour 1", Color) = (1.0, 1.0, 1.0, 1.0)
        _EdgeColor2("Edge colour 2", Color) = (1.0, 1.0, 1.0, 1.0)
        _Dissolution("Dissolution level", float) = 0
        _Edges("Edge width", Range(0.0, 1.0)) = 0.1
    }
        SubShader
    {
        Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
        LOD 100

        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
        // make fog work
#pragma multi_compile_fog

#pragma multi_compile DUMMY PIXELSNAP_ON

#include "UnityCG.cginc"

        struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float2 uv2 : TEXCOORD1;

    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float2 uv2 : TEXCOORD1;
        float4 vertex : SV_POSITION;

        UNITY_FOG_COORDS(3)
    };

    sampler2D _MainTex;
    sampler2D _NoiseTex;
    float4 _MainColor;
    float4 _MainTex_ST;
    float4 _NoiseTex_ST;
    float4 _EdgeColor1;
    float4 _EdgeColor2;
    uniform float _Dissolution;
    float _Edges;

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        o.uv2 = TRANSFORM_TEX(v.uv2, _NoiseTex);

        #ifdef PIXELSNAP_ON
        o.vertex = UnityPixelSnap(o.vertex);
        #endif
        UNITY_TRANSFER_FOG(o, o.vertex);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        float cutout = tex2D(_NoiseTex, i.uv2).r;
        fixed4 col = tex2D(_MainTex, i.uv)* _MainColor *2 ;

    if (cutout < _Dissolution)
        discard;

    if (cutout < col.a && cutout < _Dissolution + _Edges)
        col.rgb = lerp(_EdgeColor1, _EdgeColor2, (cutout - _Dissolution) / _Edges);
    
    UNITY_APPLY_FOG(i.fogCoord, col);

    return col;
    }
        ENDCG
    }
    }
}

I more so meant the C# side.

For example, try just putting this script on your object that has that material:

using UnityEngine;

public class DissolveTest : MonoBehaviour
{
    [SerializeField] private Material dissolveMat;
    [SerializeField, Range(0,1)] private float dissolveAmount;

    private void Start()
    {
        var rend = GetComponent<Renderer>().material = dissolveMat;
    }
    private void Update()
    {
        dissolveMat.SetFloat("_Dissolution", dissolveAmount);
    }
}

Press play, and start changing the DissolveAmount on this script, does it change the material look? It should.

weeird… i moved var rend = GetComponent().material = dissolveMat; to the method the materials were being swapped in rather than keeping it in Start() and now it works. i wanted to save a reference to the renderer so i could swap out materials so i put the GetComponent() in Start to initialise the reference that i would use later at run time but it wasnt working then. how odd…

Doing renderer.material = myMaterial; makes a copy of myMaterial and assigns it to the renderer component every time you call that code. The material on the object and myMaterial are now separate and completely unconnected materials. If you want to assign a material to a renderer component and have modifications to the material you assign continue to show on the object, use:
renderer.sharedMaterial = myMaterial;

1 Like

ahhh ok let me try that! i actually did get it working but yes you are correct, it animates the shader globally… which isn’t optimal but i figured i would live with it for now. your approach sounds much better.
Thanks as always bgolus!

Apologies, I know this thread is quite old, but based on my tests in Unity 2021.3, it seems that this information may no longer hold true. Assigning a material to a renderer does not create a copy of that material. Instead, the renderer directly uses the assigned material, and any modifications made to that material later will be also applied to the renderer. It appears that Unity has undergone some changes, or perhaps there is something I am overlooking.