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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Shader parameter not applied when changed at runtime

Discussion in 'Shaders' started by MBlomme, Aug 10, 2018.

  1. MBlomme

    MBlomme

    Joined:
    Dec 18, 2017
    Posts:
    11
    Hi everyone,

    I'm currently using AVPro Video for my app and I'm implementing a support for stereo 360 videos. AVPro has all the needed parameters for that but I have an issue: to enable stereo, I need to change a parameter value in the sphere's shader (passing a float from 0 to 1, parameter definition below), when I change it manually at runtime it works perfectly, but when I change it via script, the parameter is changed (I see it in the editor), but it isn't applied unless I click on it in the editor...

    I tried what I found on the internet (using Material.EnableKeyword / Shader.EnableKeyword, I even tried duplicating the shader with 1 as a default value...) but nothing worked...

    Does anybody have a solution for this?

    The parameter I need to change:
    Code (CSharp):
    1. [KeywordEnum(None, Top_Bottom, Left_Right, Custom_UV)] Stereo ("Stereo Mode", Float) = 0
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    For this kind of material property, the parameter itself usually doesn't really do anything. It's a dummy property used by the editor. They KeywordEnum is a MaterialPropertyDrawer that sets keywords on the material based on that property's value, but this only happens when changing the property from the inspector. This would explain the problems you're experiencing of setting that value from script doesn't do anything, because it doesn't by design.

    The EnableKeyword function is the path you'll need to take for setting this from script, and I suspect the problem is you're setting the wrong keyword. The actual keywords Unity will use from a KeywordEnum is constructed like this:

    PROPERTYNAME_KEYWORDNAME

    For example, the None option will set the keyword:

    STEREO_NONE

    See the documentation here:
    https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html

    If you look further down in the shader file, you should find some lines with #pragma multi_compile or #pragma shader_feature. That should list out the actual keywords as the shader uses them. Also be sure to call DisableKeyword on keywords in the same line that you're not using if you ever have them enabled. Also if the line is using a #pragma shader_feature you may have some problems with setting the keyword not working in a build, and you'll need to use a ShaderVariantCollection to make sure the build includes that variant.
     
  3. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,873
    I can't seem to make this work in 2019 ... From Unity's docs I'd always understood:

    Code (CSharp):
    1. // ...later on in CGPROGRAM code:
    2. #pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
    3. // ...
    ...that "[KeywordEnum(None,Add,Multiply)] _Overlay" generated keywords e.g. _OVERLAY_NONE.

    This works in that they are generated and can be changed through inspector. It appears broken in that explicitly setting those keywords using Material.EnableKeyword( "_OVERLAY_NONE" ) has no effect. I've tried case sensitivity - still no effect.

    In desperation, I tried OP's approach (of SetFloat( "_Overlay", 1 )), but can confirm that also has no effect (not especially surprising).
     
    Last edited: Nov 18, 2019
  4. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,873
    UPDATE:

    1. Whatever you write in your KeywordEnum line is ignored: Unity invents their own names/values instead
    2. Your #pragma multi_compile / shader_feature line MUST be written "IN ALL CAPS" for each of the names - or it will be ignored
    3. You MUST manually disable all keywords lower than the one you're enabling...
    4. ...UNLESS a human uses the mouse to click on the KeywordEnum dropdown in the Inspector - just once! - after which Unity behaves differently, and you no longer need to disable other values.

    Your shader keywords will become:

    For a KeywordEnum:
    Code (CSharp):
    1.  
    2. [KeywordEnum(One,Two)] _Number( "my number", Float ) = 0
    3.  
    1. _NUMBER_ONE
    2. _NUMBER_TWO
    NB: these are case-sensitive - even though the case you provided is NOT the same as the case they are now in.

    In case anyone's interested, this is such crappy behaviour that I've sent a bug report to Unity. They should at least fix the docs, but I'm not happy that the EnableKeyword() behaves differently based on how many times a human has pressed a button :).
     
    Last edited: Nov 17, 2019
    tonytopper and miracletechteam1 like this.
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    All keywords are disabled by default. You can really only enable and un-enable keywords, not actually "disable" them. A disabled keyword is just one that's not on the keyword list set on a material.

    In your first example you posted this:
    That won't ever set the keyword
    _OVERLAY_NONE
    since none of the enum options is None. It’ll create
    _OVERLAY_OVERLAY
    ,
    _OVERLAY_ADD
    ,
    _OVERLAY_MULTIPLY
    , but not
    _OVERLAY_NONE
    .

    Here’s the other thing about this, you should ever care if
    _OVERLAY_NONE
    is set because it shouldn’t be part of the
    multi_compile
    . Instead you should do something like this:
    Code (csharp):
    1. // property
    2. [KeywordEnum(None,Add,Multiply)] _Overlay (“Overlay Mode”, Float) = 0
    3.  
    4. // multi_compile, note the extra “_” at the start instead of NONE
    5. #pragma multi_compile _ _OVERLAY_ADD _OVERLAY_MULTIPLY
    6.  
    7. // in the shader function
    8. #if defined(_OVERLAY_MULTIPLY)
    9. return a * b;
    10. #elif defined(_OVERLAY_ADD)
    11. return a + b;
    12. #else // not using add or multiply, “none”
    13. return a;
    14. #endif
    Notice, we never check if
    _OVERLAY_NONE
    is set, nor do we have an option in the
    multi_compile
    for it. Instead it’s an implicit case of none of the other options are set. The underline as the first option of the
    multi_compile
    means make a variant without any of the subsequent keywords, which is how a material starts out.
     
    Noisecrime and tealm like this.
  6. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,873
    The missing NONE was from me typoing when copy/pasting the example here from Unity docs.

    Personally I don't use NONE variants in keyword enums :).
     
  7. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    Is there any way to get all the mutex keywords in the same multi_compile?
     
  8. lwangwangl

    lwangwangl

    Joined:
    Apr 6, 2016
    Posts:
    10
    [KeywordEnum(OFF,DIR,RECT,CIRCLE)]_GLOBALWIND("GlobalWind mode",Float) = 0

    ....

    #pragma multi_compile _GLOBALWIND_OFF _GLOBALWIND_DIR _GLOBALWIND_RECT _GLOBALWIND_CIRCLE

    and When i called Shader.EnableKeyword/DisableKeyword
    It still not work
     
  9. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    199
    Having similar difficulties in Shader Graph. I believe this is bugged. Hard to be 100% sure because this feels like a half-baked and not well-designed API.

    If this is a shader-level feature why is it being called on the Material? It seems to work sometimes on a Material instance level, which doesn't seem very good because I could just use a Material property in that case. Even though that's not quite the same under the hood.

    I also couldn't get the updates to appear in Play Mode, but once I exited Play Mode the changes appeared in Scene View. I verified all the right keywords are getting enabled and disabled via logging to the console.

    The OP is from four years ago so it's concerning that this still isn't working well.

    I suspected it had something to do with a Shader compilation issue of some sort. So I decided to disable Asynchronous shader compilation, after much weeping and gnashing of teeth trying other things, and low and behold things started working in Play Mode.
     
  10. MutoXicated

    MutoXicated

    Joined:
    Sep 5, 2022
    Posts:
    2
    Can you tell me what you did to make it work? I've also disabled asynchronous shader compilation but it still doesn't want to change the shader paramater. Thanks in advance.