Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

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

Discussion in 'Scripting' started by PsycheMac, May 12, 2017.

  1. PsycheMac

    PsycheMac

    Joined:
    Apr 15, 2013
    Posts:
    32
    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:

    Code (CSharp):
    1. //In C# code
    2. public void SetRiverTexture(bool bFlipX,bool bFlipY,Sprite riverSprite)
    3. {
    4.      spriteRenderer.material.SetTexture("_RiverTex",riverSprite.texture);
    5.  
    6.      if(bFlipX)
    7.           spriteRenderer.material.SetFloat("_FlipX",1f);
    8.  
    9.      if(bFlipY)
    10.           spriteRenderer.material.SetFloat("_FlipY",1f);
    11. }
    12.  
    13. //In shader
    14. Properties
    15. {
    16.      _RiverTex ("River Texture", 2D) = "white" {}
    17.      [Toggle] _FlipX ("Flip River X", Float) = 0
    18.      [Toggle] _FlipY ("Flip River Y", Float) = 0
    19. }
    20.  
    21. CGPROGRAM
    22. #pragma multi_compile _FLIPX_OFF _FLIPX_ON
    23. #pragma multi_compile _FLIPY_OFF _FLIPY_ON
    24.  
    25. sampler2D _RiverTex;
    26.  
    27. void surf (Input IN, inout SurfaceOutput o)
    28. {
    29.     fixed2 uv = IN.uv_MainTex;
    30.  
    31.     #if _FLIPX_ON
    32.         uv.x = 1.0 - uv.x;
    33.     #endif
    34.  
    35.     #if _FLIPY_ON
    36.         uv.y = 1.0 - uv.y;
    37.     #endif
    38.  
    39.     fixed4 c = tex2D (_RiverTex, uv);
    40.          
    41.     o.Albedo = c.rgb * c.a;
    42.     o.Alpha = c.a;
    43. }
    44. ENDCG
     
  2. PsycheMac

    PsycheMac

    Joined:
    Apr 15, 2013
    Posts:
    32
    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.
     
  3. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    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 :
    Code (CSharp):
    1. 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.
     
  4. PsycheMac

    PsycheMac

    Joined:
    Apr 15, 2013
    Posts:
    32
    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.

    Code (CSharp):
    1. //Flip X and Y will either be 0 or 1.
    2. uv.x = _FlipX - (uv.x * (-1 + _FlipX * 2));
    3.  
    4. uv.y = _FlipY - (uv.y * (-1 + _FlipY * 2));
     
  5. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Have you tried using Material.EnableKeyword? That was the way you would choose multi-compile paths in the past.
     
  6. PsycheMac

    PsycheMac

    Joined:
    Apr 15, 2013
    Posts:
    32
    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:

    Code (CSharp):
    1. //In C#
    2. spriteRenderer.material.EnableKeyword("_FLIPX_ON");
    3.  
    4. //In shader
    5. #pragma multi_compile _FLIPX_OFF _FLIPX_ON
    6.  
    7. ...
    8.  
    9. #if _FLIPX_ON
    10.      uv.x = 1 - uv.x;
    11. #endif