Search Unity

Can't access _SpecularHighlights via script. PLEASE, help!

Discussion in 'Getting Started' started by kbr0n, Sep 9, 2016.

  1. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    hello world

    I need to control these 2 properties from the Standard Shader, while the project is running:

    _SpecularHighlights -> which appears in the Inspector as checkbox named 'Specular Highlights'

    and

    _GlossyReflections -> which also appears in the Inspector as a checkbox named just 'Reflections'

    So I need a GUI checkbox that the user can toggle on/off while the project is running.

    I already managed to do a similar script that controls other properties of the shader, as _GlossMapScale, but the same approach is not working for _SpecularHighlights or _GlossyReflections

    so, this works:
    Code (csharp):
    1. shapes.GetComponent<Renderer> ().material.SetFloat ("_GlossMapScale", sldfind.value);
    but this does not:
    Code (csharp):
    1.  shapes.GetComponent<Renderer> ().material.SetFloat("_SpecularHighlights", 0);
    I've been trying to get this right for weeks, PLEASE, someone help me!
    thank you
     
    Last edited: Sep 16, 2016
  2. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    bump!
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I'm stumped; your code looks correct to me. You should try asking this in the Shaders forum.
     
    Kiwasi and kbr0n like this.
  4. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    Not really sure either. The below test code works for me.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class testSettings : MonoBehaviour {
    5.  
    6.     public bool highlights = true;
    7.     public bool glossReflections = true;
    8.  
    9.     void LateUpdate () {
    10.         gameObject.GetComponent<Renderer>().material.SetFloat("_SpecularHighlights", highlights ? 1f : 0f);
    11.         gameObject.GetComponent<Renderer>().material.SetFloat("_GlossyReflections", glossReflections ? 1f : 0f);
    12.     }
    13. }
    14.  
    It's possible you need to specifically inherit from 'shapes.gameObject' instead of just 'shapes'. It's also possible it's not adjusting the renderer that you think it is. If you attach the above code directly to the object then it should work, you might need to do some hunting from there to see exactly what is amiss in your own code.
     
    JoeStrout likes this.
  5. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    hello chingwa, thank you so much for your reply

    It´s the best I got so far, but still, not working as expected

    I´m in a hurry right now, so I´ll have to perform further tests later. I added the script above directly to an object:

    when I uncheck the 'public bools' on the script, the SpecularHigh and Reflections are toggled off but there's no change in the object itself. It only changes when I click directly on their checkboxes, under "Forward Rendering Options"

    so it appears unchecked, but does not update

    and why SetFloat will work with 'true' and 'false' as a variable and not directly with SetFloat("_parameter", true)???

    I don't understand what the "? 1f: 0f" do
     
  6. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    The bool ? floatA : floatB pattern is a convenience expression that automatically converts floats out of the given boolean. So the line is still sending floats to the shader. floatA gets passed when the boolen is positive, and floatB gets passed when the boolean is negative. This way you don't have to write extra code when you just want to pass set values for true/false. You can use this expression pattern pretty much anywhere, not just with shader value assignments.

    Not sure what is going on with the Standard shader values though. I didn't test on my end whether they were actually doing anything shader-wise, I just looked at if the values were being sent properly. :(
     
  7. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    aaa ok! got it!

    no problem, chingwa. I'm starting to believe this may be a bug in Unity...

    but the big question is answered! in order to set SpecularHighs and GlossyReflections on/off, one should use SetFloat("_parameter", 1 or 0)

    thank you
     
  8. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    Finally! I manage to get this working using keywords and setfloat

    like this:

    to turn ON:

    Code (csharp):
    1.  shapes.GetComponent<Renderer>().material.DisableKeyword("_SPECULARHIGHLIGHTS_OFF");
    2. shapes.GetComponent<Renderer>().material.SetFloat("_SpecularHighlights",1f);
    to turn OFF:

    Code (csharp):
    1.  shapes.GetComponent<Renderer>().material.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
    2. shapes.GetComponent<Renderer>().material.SetFloat("_SpecularHighlights",0f);
    - it's tricky cause there's no "_specularhighlights_on", instead you disable or enable "_specularhightlights_off"

    I created an array for objects that are tagged "shapes", so this code should work for every objects in this array.

    problem now is that some objects are children, and for these objects, the code is not working properly.
    I think I need a different way to access them, but how can I do that if they are already in a array?
     
    lclemens and JoeStrout like this.
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Nice job finding that keyword. Never would have guessed that myself.

    Now that you've done that, it sounds like you're stuck on (to me) much easier issues of finding references to things. What do you mean "they are already in an array"? Can you show us your object hierarchy, and what you're trying to do?

    It might be as easy as using GetComponentsInChildren<Renderer>().
     
  10. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    Hi, Joe! Thank you! the moment I clicked that checkbox and it worked..omg, only coders know the feel hahaha

    and yes, I think this next problem is a simpler one! (I hope)

    I have several objects (sphere, cube, cylinder etc) that can be selected via a drop down menu (wich toggles the object on/off) and some controls that should apply to all objects, like Glossiness, SpecularHighLights, Reflections etc

    So I created an array with all these objects and apply changes using a for each loop.

    The thing is I had nested objects, e.g. 2 cilinders, one as child of the other, and an imported .obj file that also has children.

    the script worked fine with all objects, except on the children. Only the parent got affected, but once it was turned off ( specularhighs) I could not turn it back on again.

    unity-hierar.jpg


    I mean, I´m currently declaring the array like this:

    Code (csharp):
    1.  
    2.   GameObject[]shapeobjects=GameObject.FindGameObjectsWithTag("shapes");
    3. foreach(GameObject shapes inshapeobjects){
    4.  
    5.  
    6. if(onoff){
    7.  
    8. shapes.GetComponent<Renderer>().material.SetFloat("_SpecularHighlights",1f);
    9. shapes.GetComponent<Renderer>().material.DisableKeyword("_SPECULARHIGHLIGHTS_OFF");
    10.  
    11. onoff= !onoff;
    12.  
    13. }
    14.  
    15. else{
    16. shapes.GetComponent<Renderer>().material.SetFloat("_SpecularHighlights",0f);
    17. shapes.GetComponent<Renderer>().material.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
    18.  
    19. onoff= !onoff;
    20. }
    21.  
    22.  
    23. }
    24.  


    I should an extra line with the GetComponentInChildren?

    I got it but not sure how to do it...
     
    Last edited: Sep 21, 2016
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well, there's a logic error in that code... look at when you're toggling onoff. You're flipping it after every object. I'm pretty sure you only want to flip it once, after the loop.

    Also, when scratching your head over something like this, add some Debug.Log calls to help you see what your code is doing. The results in this case would surprise you. :) So, inside the if-block where you turn the highlights on, add:

    Code (csharp):
    1. Debug.Log("Turned highlights ON for " + shapes.name, shapes);
    and put a similar line in the if-block that turns them off (but saying OFF instead of ON, of course).

    I think this will make it clear what's going on.
     
  12. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    +1 to what @JoeStrout said... also I would make sure that none of these objects are using the same material, or you may be be overriding the previous settings that you make in the loop. As an alternative, in the case of objects using the same material, you can use sharedMaterial in order to make an instanced material that will respect different settings per object, like so:

    Code (CSharp):
    1.  
    2. shapes.GetComponent<Renderer>().sharedMaterial.SetFloat("_SpecularHighlights",0f);
    3. shapes.GetComponent<Renderer>().sharedMaterial.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
     
    JoeStrout likes this.
  13. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    @JoeStrout
    right on!! I knew I shouldn't have added those 2 flippers hehehe - that's solved the problem! now, every object behaves equally - thank you, mr. Joe!

    @chingwa
    oops, they all share the same material! but that's intended, cause I want a single control to affect all objects at the same time - but only one is seen at a time, tho.

    but I´m having a small issue that may be related to this:

    I have a checkbox that toggles the specularhighlight state. If I´m seeing the sphere and turn specularhighs off, it remains unchecked. Fine. But than I select the 'cube' in the dropdown menu. The sphere is gone, now the cube appears. The checkbox is still unchecked but the specularhighlights for the cube is 'on'.

    now I need to make the gui checkbox copy the state of the current selected object, tho I´m not sure why this is happenning. If all of them share the same material, shouldn't all of them be in the same state? not only because of that, but because the for each loop as well.

    or inactive objects are not affected? that would explain it, since the 'onchange' action for the dropdown hides all objects but the one selected.

    Thank you for all the help!
     
  14. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    When you access .material, and it's a material that is shared with other objects, then Unity clones the material for you on the spot, and returns a reference to the clone. (When that's not what you want, you should use .sharedMaterial instead.)

    It sounds like you should forget about getting renderers and iterating over objects entirely — if they really do all share the same material, then just give your script a reference to this material (it lives in the project), and change that. All objects will automatically get the changes, because they're all using that same material (which you will no longer be cloning).
     
    chingwa likes this.
  15. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,790
    Agree. If they all use the same material you don't need to iterate. Just change it on the material once.
     
  16. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    wow, that's some good news!
    gonna try that

    should I create a 'public Material' to do that or there's a more direct way to access this material?
     
  17. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Just create the public Material property; I think that's the simplest way.
     
  18. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    hello, unity pals!

    new challenge!
    I´m have some sliders on my project to control the color of lights. There's also checkboxes to turn each light on and off.

    Now I´m trying to change the color of the checkboxes according to the light colors. So if the light 1 is blue, the checkbox1 will also be blue and so on.

    It´s already 'half working', but the checkbox it's not updating in real time.

    unity-tog.jpg

    and the script (which alreadys changes the main light color)

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5. using UnityEngine.UI;
    6.  
    7. //scrip tdos sliders de cor da luz 1.
    8.  
    9. public class GUITest : MonoBehaviour{
    10.  
    11. public Color myColor;
    12. public Light luz;
    13. public float ycoord;
    14. public float xcoord;
    15. public Toggle ligtoggle;
    16.  
    17.  
    18.  
    19. voidStart()
    20. {
    21. luz=luz.GetComponent<Light>();
    22. ycoord=30;
    23. xcoord=10;
    24.  
    25. }
    26.  
    27.  
    28. voidOnGUI(){
    29.  
    30.  
    31.  
    32. myColor=RGBSlider(newRect(xcoord,ycoord,200,10),myColor);
    33. luz.color=myColor;
    34. ligtoggle.image.color=myColor;    // shouldn't this work?!
    35.  
    36. }
    37.  
    38. // GUI RGB slider
    39.  
    40. ColorRGBSlider(RectscreenRect,Colorrgb){
    41. rgb.r=GUI.HorizontalSlider(screenRect,rgb.r,0.0f,1.0f);
    42.  
    43. //<-Movethenextcontroldownabittoavoid overlapping
    44. screenRect.y+=20;
    45. rgb.g=GUI.HorizontalSlider(screenRect,rgb.g,0.0f,1.0f);
    46.  
    47. //<-Movethenextcontroldownabittoavoid overlapping
    48. screenRect.y+=20;
    49.  
    50. rgb.b=GUI.HorizontalSlider(screenRect,rgb.b,0.0f,1.0f);
    51. returnrgb;
    52.  
    53.  
    54. }
    55.  
    56. }
    57.  
    58.  
     
  19. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Don't use the old UI methods (OnGUI, GUI.HorizontalSlider, etc.). Use the new Canvas-based UI.
     
  20. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    I found this code (for the RGB sliders) somewhere, I tried but I don't know how to do it using gui sliders. :(

    it's working for the light color, can't figure why it's not working for the image.color

    I´m going for something like:


    Code (csharp):
    1.  
    2. public Slider Rslider;
    3. public Slider Gslider;
    4. public Slider Bslider;
    5. public Color myColor
    6.  
    than use the slider values to create myColor = (value1, value2, value3)

    ??
     
    Last edited: Sep 24, 2016
  21. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Then you really need to go through some of the UI tutorials. Please believe me, it's time well spent. The new UI system is much easier than the old one, and not hard to learn, and you'll use it in pretty much every project you ever do.
     
  22. kbr0n

    kbr0n

    Joined:
    Aug 28, 2016
    Posts:
    24
    thank you, Joe!

    I wasn't thinking about changing those sliders but I´m glad you told me to do so. I got them working already and things are running smooth now :cool:
     
    JoeStrout likes this.
  23. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    I had a similar issue, where material instance was not updating reflections until we expand/collapse the rollout using the inspector. Turns out we needed these lines:


    Code (CSharp):
    1. var hasGlosReflect = fromMaterial.IsKeywordEnabled("_GlossyReflections") || fromMaterial.GetFloat("_GlossyReflections")==1.0f;
    2.  
    3. SetKeyword(toMaterial, "_GlossyReflections", hasGlosReflect);
    4. toMaterial.SetFloat("_GlossyReflections", hasGlosReflect?1.0f:0.0f);
    5. SetKeyword(toMaterial, "_SPECULARHIGHLIGHTS_OFF", !hasGlosReflect);//notice using ! because '_OFF' suffix in the string
    6. SetKeyword(toMaterial, "_GLOSSYREFLECTIONS_OFF",  !hasGlosReflect);
    7.  
    8. private static void SetKeyword(Material m, string keyword, bool state){
    9.             if (state)  m.EnableKeyword(keyword);
    10.             else   m.DisableKeyword(keyword);
    11. }
    12.  

    https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html

    https://forum.unity.com/threads/nee...t-access-them-via-script.431533/#post-2795810