Material.SetTexture() applied per material instance but Material.SetFloat() is not

I’m trying to set shader parameters in a material at runtime and I would like them to be applied uniquely per instance of the material. I’m referencing the renderer and it’s material so this should create a material instance when modifying these parameters.

Currently I am successfully able to use Material.SetTexture() to set a different texture per material instance but I am not able to do this with Material.SetFloat(). That will end up setting the float for all objects with that material rather than setting it per instance. How do I get Unity to set the float per instance rather than across all objects using that material?

Here is some of the code I’m using. I edited down the shader code to just show the more relevant parts:

//In C# code
public void SetRiverTexture(bool bFlipX,bool bFlipY,Sprite riverSprite)
{
     spriteRenderer.material.SetTexture("_RiverTex",riverSprite.texture);

     if(bFlipX)
          spriteRenderer.material.SetFloat("_FlipX",1f);

     if(bFlipY)
          spriteRenderer.material.SetFloat("_FlipY",1f);
}

//In shader
Properties
{
     _RiverTex ("River Texture", 2D) = "white" {}
     [Toggle] _FlipX ("Flip River X", Float) = 0
     [Toggle] _FlipY ("Flip River Y", Float) = 0
}

CGPROGRAM
#pragma multi_compile _FLIPX_OFF _FLIPX_ON
#pragma multi_compile _FLIPY_OFF _FLIPY_ON

sampler2D _RiverTex;

void surf (Input IN, inout SurfaceOutput o)
{
    fixed2 uv = IN.uv_MainTex;

    #if _FLIPX_ON
        uv.x = 1.0 - uv.x;
    #endif

    #if _FLIPY_ON
        uv.y = 1.0 - uv.y;
    #endif

    fixed4 c = tex2D (_RiverTex, uv);
         
    o.Albedo = c.rgb * c.a;
    o.Alpha = c.a;
}
ENDCG

Is the problem unclear or need better explanation? Currently the only work around I have is to make a separate material for each customization of the shader that I want but I would much rather do this at runtime.

This should probably go into the graphics section…
To me what you describe sounds like a bug, but I’m no expert really.
Why don’t you declare the floats normally for now (without multi_compile) and merge in the conditions like this :

uv.x = 1.0 - flipX * uv.x;

?
I don’t see how the performance of such a small shader can ever be an issue nowadays.

Well I took your idea of not using multi_compile and using SetFloat with a regular float in the CGPROGRAM and it does allow it to work properly. Here is what I’m using, I don’t like it that much so if anyone knows why multi_compile floats are only being set globally and not per instance that would be helpful to know. Id much rather use them as booleans rather than actual floats.

//Flip X and Y will either be 0 or 1.
uv.x = _FlipX - (uv.x * (-1 + _FlipX * 2));

uv.y = _FlipY - (uv.y * (-1 + _FlipY * 2));

Have you tried using Material.EnableKeyword? That was the way you would choose multi-compile paths in the past.

Aha, I had tried that before and it didn’t work but I think I had been doing it wrong. I was trying to enable the material’s reference to the float (“_FlipX”) and not the actual key word (“_FLIPX_ON”). I just deleted the float toggle for the material since I don’t actually need it and instead referenced the multi_compile’s keyword. So this is the correct way:

//In C#
spriteRenderer.material.EnableKeyword("_FLIPX_ON");

//In shader
#pragma multi_compile _FLIPX_OFF _FLIPX_ON

...

#if _FLIPX_ON
     uv.x = 1 - uv.x;
#endif