Search Unity

Question MaterialPropertyBlock wont set colors properly...

Discussion in 'Scripting' started by DanielRiches, Aug 24, 2020.

  1. DanielRiches

    DanielRiches

    Joined:
    Nov 23, 2019
    Posts:
    166
    Code is below, fully annotated so you can follow step-by-step

    The _EmissiveColor property in the MPB wont set correctly, despite me saving the mesh _EmissiveColor and setting it to match, i needed to lerp the brightness of a mesh up then back down to it's default brightness.

    For testing purposes, I set it to lerp from the mesh starting _EmissiveColor, to the same, so according to this code, there should be no change in brightness at all.

    The result:

    Before the lerp, mesh default brightness:


    After the lerp starts:


    As you can see theres a clear difference in brightness there, i'm not sure why the MPB wont copy the color exactly, I desperately need this to work :( can anyone out there help me?


    Code (CSharp):
    1. private Renderer myMeshRenderer;
    2.  
    3. [Header("- Material Target -")]
    4. public int materialElement;
    5. private Material meshMaterial;
    6.  
    7. [Header("- Default Properties -")]
    8. [SerializeField] private Color startingMeshColor;
    9. [SerializeField] private Color startingEmissionColor;
    10.  
    11. public bool lerpEmissionIntensity;
    12.  
    13. bool lerpingIntensityDown;
    14. float eiLerpDuration;
    15. float eiLerpTimer;
    16. float eiLerpElapsedTime = 0f;
    17.  
    18.  
    19.     public void Start()
    20.     {
    21.         theMaterial = new MaterialPropertyBlock();
    22.         theParticle = new MaterialPropertyBlock();
    23.  
    24.         // Get Renderers
    25.         myMeshRenderer = GetComponent<Renderer>();
    26.            
    27.         if (myMeshRenderer != null)
    28.         {
    29.             // Get chosen element properties from the Mesh renderer and assign it to theMaterial MaterialPropertyBlock
    30.             myMeshRenderer.GetPropertyBlock(theMaterial, materialElement);
    31.  
    32.             // If you accidentally choose a non-existant element
    33.             if (materialElement > myMeshRenderer.materials.Length - 1)
    34.             {
    35.                 Debug.Log("Selected a non-existant material on " + this.gameObject.name + ", Element 0 selected as default");
    36.                 materialElement = 0;
    37.             }
    38.  
    39.             // Cache mesh material to copy default values, I WISH MPB's did this by default, that way there would be no need for this at all
    40.             meshMaterial = myMeshRenderer.materials[materialElement];
    41.  
    42.             // Set mesh default values, this will detect which property the mesh uses for it's base color and sets starting color field accordingly
    43.             if (meshMaterial.HasProperty("_BaseColor"))
    44.             {
    45.                 startingMeshColor = meshMaterial.GetColor("_BaseColor");
    46.             }
    47.             else
    48.             {
    49.                 startingMeshColor = meshMaterial.GetColor("_Color");
    50.             }
    51.  
    52.             // Apply the base color to the appropriate MaterialPropertyBlock field
    53.             theMaterial.SetColor("_Color", startingMeshColor);
    54.  
    55.             // If mesh material has emission field, record default color and set MaterialPropertyBlock field to match
    56.             if (meshMaterial.HasProperty("_EmissiveColor"))
    57.             {
    58.                 startingEmissionColor = meshMaterial.GetColor("_EmissiveColor");
    59.        
    60.                 theMaterial.SetColor("_EmissiveColor", startingEmissionColor); // This SHOULD set _EmissiveColor field of MPB to exactly the same as the mesh Emissive but doesnt?
    61.             }
    62.         }
    63.  
    64.         // Apply Starting Edited Values To Appropriate Renderer Material Element.
    65.         if (myMeshRenderer != null)
    66.         {
    67.             myMeshRenderer.SetPropertyBlock(theMaterial, materialElement);
    68.         }
    69.     }
    70.  
    71.  
    72. public void Update()
    73. {
    74.     // Get Current Values Of Material Properties In theMaterial.
    75.     if (myMeshRenderer != null)
    76.     {
    77.         myMeshRenderer.GetPropertyBlock(theMaterial, materialElement);
    78.     }
    79.  
    80.     if (lerpEmissionIntensity)
    81.     {
    82.        if (eiLerpElapsedTime >= eiLerpDuration)
    83.        {
    84.           if (!lerpingIntensityDown)
    85.           {
    86.              lerpingIntensityDown = true;                              
    87.              eiLerpElapsedTime = 0f;
    88.           }                          
    89.           else
    90.           {
    91.              lerpingIntensityDown = false;                          
    92.              eiLerpElapsedTime = 0f;
    93.           }                          
    94.        }
    95.  
    96.        if (!lerpingIntensityDown)
    97.        {
    98.           eiLerpElapsedTime += Time.deltaTime;
    99.           eiLerpTimer = eiLerpElapsedTime / eiLerpDuration;
    100.           theMaterial.SetColor("_EmissiveColor", Color.Lerp(startingEmissionColor, startingEmissionColor * 1.5f, eiLerpTimer));
    101.        }                      
    102.        else
    103.        {                          
    104.           eiLerpElapsedTime += Time.deltaTime;
    105.           eiLerpTimer = eiLerpElapsedTime / eiLerpDuration;
    106.           theMaterial.SetColor("_EmissiveColor", Color.Lerp(startingEmissionColor * 1.5f, startingEmissionColor, eiLerpTimer));
    107.        }                      
    108.     }
    109.  
    110.     // Apply Edited Values To Appropriate Renderer Material Element.
    111.     if (myMeshRenderer != null)
    112.     {
    113.         myMeshRenderer.SetPropertyBlock(theMaterial, materialElement);
    114.     }
    115.  
    116. }
    Thank you in advance for any advice!
     
    Last edited: Aug 24, 2020
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    Is it perhaps not so much a color issue as it is perhaps an alpha issue?

    In any case, I would start by printing out some of your calculated color values (using Debug.Log()) before you write them back to the MPB fields. I bet you'll find which ones are either suspiciously low intensity, or alpha missing, and then you can just chase up the variables that contributed to this outcome until you find the bad data, printing it out at each step up the chain so to speak.

    EDIT: Just noticed another potential issue: Color32() numbers are ranged from 0 to 255 while Color() numbers are ranged from 0.0f to 0.1f;

    I see you mixing and matching those, are you doing so correctly? Usually you want to choose one or the other just for sanity.
     
  3. DanielRiches

    DanielRiches

    Joined:
    Nov 23, 2019
    Posts:
    166
    Thanks for your Input, i'll print some Debug.Log's and see whats happening.

    as for the color/color 32 thing, i was using anything emissive as color32 but using color for basic mesh color, but i'll switch it all to Color32 so it's uniform
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    I seem to remember if you just assign a Color to a Color32 (or vice versa) Unity just takes care of it, probably by virtue of it being the same underlying structure at the native level. It's only when you pluck out the values that you need to care.
     
  5. DanielRiches

    DanielRiches

    Joined:
    Nov 23, 2019
    Posts:
    166
    Ok, something VERY WEIRD is going on lol

    So first I did a Debug.Log of the startingEmissionColor, after setting the MPB I then read the MPB and I noticed that it was outputting in Color, not Color32, so I changed all my formats to match. I suspect MPB's just dont do Color32? At this stage everything is now matching.

    I then added a Debug.Log just after GETTING the values from the MPB in Update() line (77 - 81) and it outputted at black?!



    I was following a tutorial at https://thomasmountainborn.com/2016/05/25/materialpropertyblocks/ , and he says you should get the current values at the start of every frame then re-apply at the end of every frame (as you see in the code I posted)

    The curious thing is, commenting out lines 77 - 81 and THEN getting the MPB _emissionColor outputs exactly right??



    Then it gets even WEIRDER!, for some reason, after commenting out those lines and hitting play the _emissionColor of the MPB suddenly goes to exactly half the brightness originally intended, so I thought "well, nothing even happens in Update() until I press a button, so perhaps setting the MPB at the end of Update() has somehow halved the brightness?

    So I commented that out too......no effect......it's still half brightness, so that cant be the cause.

    And between getting the properties, and setting the properties, literally nothing happens until I tell it to do something..

    So to summarize, getting the properties at the beginning of each frame means the mesh starts at intended brightness, but any properties it DOES get are black (despite setting them previously in start)

    If this is the case, no matter what I do to set the property, it will ALWAYS seemingly read as black:



    NOT getting the properties at the start of each frame allows you to read the MPB at correct colors, but the mesh is now half the starting brightness....for some reason...

    Also, despite me using startingMeshColor * 1.5f as the target upper color, it seemingly wants to lerp it to half the color brightness instead? am I doing that wrong somehow??

    Mega confusion here XD
     
    Aldeminor likes this.
  6. DanielRiches

    DanielRiches

    Joined:
    Nov 23, 2019
    Posts:
    166
    UPDATE:

    I realised on line 77 - 81 I forgot to use the appropriate materialElement, so I changed that, now you CAN read it and it DOES output the correct color.

    The brightness issue still remains though, it immediately halves the mesh brightness when hitting play for some reason...

    Seems the issue I had when NOT getting those properties correctly, now happens when I DO get them correctly after sorting out the materialElement issue...

    It also still wants to lerp to half the brightness lol
     
    Last edited: Aug 24, 2020
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    I haven't used the specific properties you are referring to, however if you are familiar with the basic particle additive material, it is set up so that 1.0 intensity is done with 0.5 values on rgba. This gives you solid white. I believe they do this to give you some headspace to overdo a color beyond 1.0 when mixing it could make sense if the alpha is lower... not sure.

    Is that perhaps what is going on with your emissive? See pic: That half-intensity gray gives full white particles.

    Screen Shot 2020-08-24 at 10.09.26 AM.png
     
  8. DanielRiches

    DanielRiches

    Joined:
    Nov 23, 2019
    Posts:
    166

    After asking a friend for some help, he managed to nail down the issue!

    the culprit was :

    theMaterial.SetColor("_EmissiveColor", startingEmissionColor);

    apparently, it performs properly when using .SetVector instead, not only properly, but much more smoothly too!

    So using .SetVector on any color change at least anywhere in your code is the way to do it!

    ** Also using .GetVector when getting color for consistency, but we couldnt see any real difference with the Get

    Hopefully this will guide others who are experimenting with MPB's

    And thanks for your assistance.
     
    laurienash, Kurt-Dekker and mopthrow like this.
  9. laurienash

    laurienash

    Joined:
    Mar 15, 2013
    Posts:
    61
    Really helpful answer! I had strange brightness issues when using SetColor - and SetVector has fixed it for me!
     
    moustachecabal likes this.