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

Resolved Set DecalProjector texture from script not working

Discussion in 'High Definition Render Pipeline' started by cecarlsen, Nov 24, 2021.

  1. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    Perhaps a bug? Or maybe I am doing it wrong?

    I set a texture on the material of a DecalProjector from script using material.SetTexture. No result. I check the inspector for the material and I clearly see the texture that I've just set. I click ANY setting in the inspector for the material and BOOM the texture is applied and I see the result in the scene.

    So I looked at DecalProjectorEditor.cs in the hope that I would find some magic function that is called around EditorGUI.EndChangeCheck() that would update some magic undocumented state in the decalprojector. But I found none.

    So. What is the corret way of setting the texture of a decal projector from script in HDRP (v. 12.1)?

    EDIT: I also tried calling OnValidate on the DecalProjector using reflection after setting the texture on the material. No luck.
     
    Last edited: Nov 24, 2021
  2. chap-unity

    chap-unity

    Unity Technologies

    Joined:
    Nov 4, 2019
    Posts:
    717
    hey, if you set a material property at runtime, you need to enable its associated keyword for Unity to know which shader variant to use.

    If you're using the newest version of HDRP, there's been a new static function that helps you with that.
    What it does, is the same that what is done when fiddling with inspector, i.e go through the properties of the material and adds/remove missing keywords so that Unity can find the proper variant.
    Code (CSharp):
    1. using UnityEngine.Rendering.HighDefinition;
    2. using UnityEditor.Rendering.HighDefinition;
    3. ...
    4. HDMaterial.ValidateMaterial(this.GetComponent<DecalProjector>().material);
    On older version, you can just enable the proper keyword, "by hand". In your case it would something like that.

    Code (CSharp):
    1. using UnityEditor.Rendering.HighDefinition;
    2. ...
    3. this.GetComponent<DecalProjector>().material.EnableKeyword("_COLORMAP");
    If you want to find another keyword, you can use the editor in debug mode when selecting a material and see what keyword is enabled in editor so that you can activate them at runtime.

    And it is not specific to HDRP at all, it will behave the same way in Built-in for example.
     

    Attached Files:

  3. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    Thanks @chap-unity that is exactly what I was looking for. I just had to enable "_NORMALMAP" and "_MATERIAL_AFFECTS_NORMAL" using material.EnableKeyword().

    I'm on HDRP 12.1 so no luck there. And is it not part of the UnityEditor assembly? How would that help at runtime?

    I have used Unity for years, and somehow this case surprises me. Perhaps because I tend to access materials indirectly through components that use them, or because in most cases I write my own components that manage custom materials/shaders (and their keywords/variants). From a UX perspective, keeping track of which keywords to enable and disable really should not be my problem as a user of a build-in tool. I would prefer to set decal properties on the decal projector, not the material. However, I do understand it would be misleading if a property I set on the decal projector happen to be unsupported by the particular material/shader it holds. Perhaps there is some kind of layer missing for runtime. Something that does the job of the component inspector; enabling and disabling keywords.
     
    Last edited: Nov 25, 2021
  4. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    In case others run into this too ...

    I didn't want my texture generating MonoBehaviour to deal with this. It should be ignorant to the reciever and just send out the texture using a UnityEvent. So I ended up writing this little helper script to decouple the code. I still think the job of keeping track of keywords should not be put on users, and so I consider this script a necessary evil until a more user firendly solution is found.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [ExecuteInEditMode]
    4. public class SetTextureOnMaterial : MonoBehaviour
    5. {
    6.     [SerializeField] Material _material;
    7.     [SerializeField] string _texturePropertyName = "_MainTex";
    8.     [SerializeField,Tooltip( "i.e. _COLORMAP, _MASKMAP, _NORMALMAP, _MATERIAL_AFFECTS_NORMAL and such." )] string[] _associatedKeywords;
    9.     [SerializeField] Texture _texture;
    10.  
    11.     bool _textureChanged;
    12.  
    13.  
    14.     public Texture Texture
    15.     {
    16.         get { return _texture; }
    17.         set {
    18.             _texture = value;
    19.             _textureChanged = true;
    20.         }
    21.     }
    22.  
    23.  
    24.     void Start()
    25.     {
    26.         _textureChanged = true;
    27.     }
    28.  
    29.  
    30.     void Update()
    31.     {
    32.         if( _textureChanged ) {
    33.             if( _material ){
    34.                 _material.SetTexture( _texturePropertyName, _texture );
    35.                 if( _texture ) foreach( string keyword in _associatedKeywords ) _material.EnableKeyword( keyword );
    36.                 else foreach( string keyword in _associatedKeywords ) _material.DisableKeyword( keyword );
    37.             }
    38.             _textureChanged = false;
    39.         }
    40.     }
    41.  
    42.  
    43.     void OnValidate()
    44.     {
    45.         _textureChanged = true;
    46.     }
    47. }
     
    Last edited: Nov 25, 2021
    chap-unity and PutridEx like this.
  5. chap-unity

    chap-unity

    Unity Technologies

    Joined:
    Nov 4, 2019
    Posts:
    717
    It will help in editor in play mode because, it will compile the shader variant on the fly and then be able to find it because the keyword are properly setup, so you'll get your material displayed as expected.

    However, when bulding a standalone, you need to have the shader variant already present (having a material using that variant in your asset) because it won't be able to compile the variant on the fly, so no shader variant present and no way to find it will result in missing material feature.

    I completely agree with that, and that is the reasons behind the "HDMaterial.ValidateMaterial" function that does exactly what the inspector is doing when you use a new shader feature (read, add a normal map) to avoid users to do it themselves.
     
  6. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    Sorry to sound critical, but this "solution" cause more problems than it solves. Tell me this ...

    1) Why was this solution designed to only work in the editor? This means all the solutions I build on top will also only work in the editor.

    2) Why was this solution made specifically for HDRP? Is this a a temporary fix while we wait for a global cross-RP solution ... or can I expect to see novel solutions for every RP? Will I see UMaterial.ValidateMaterial? Unity is already being torn into what feels like three very separate and incompatible engines. Please tell me how this is not another example of this.
     
    Last edited: Dec 14, 2021
  7. chap-unity

    chap-unity

    Unity Technologies

    Joined:
    Nov 4, 2019
    Posts:
    717
    The point of the forum is to get feedback from the community, so it's okay to be critical !

    This solution is designed to work only in editor because we don't have any other choice. Unity (not only in HDRP) forbids to compile shader variants in a standalone because of performance reasons (it can cause player freeze etc...).

    For this point, I personally don't have the big picture, maybe you will have better luck elsewhere in the forum rather than on the HDRP specific section of the forum.
     
  8. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    I was under the impression that all possible shader variants are compiled at build time, while at build runtime the correct shader is chosen by evaluating the enabled and disabled keywords. How else would a simple script like the one I posted above work when the keyword states are unknown at build time?

    This only adds to my fear that Unity is becoming departmentalised to the point where no one knows what's happening next door. Each department may be solving the exact same problem, but invent their own novel solution. Meanwhile the user has to suffer the consequence, and learn all the solutions. This kills creativity, because energy is wasted on figuring out how to do things, rather than iterating on an idea.

    It is painful to watch how an engine I love for it's simplicity is becoming unnecessarily complex. Not because people used to be smarter, but perhaps because they sit in isolated silos and don't coordinate.
     
  9. Remy_Unity

    Remy_Unity

    Unity Technologies

    Joined:
    Oct 3, 2017
    Posts:
    644
    Unity will compile at build time only the "needed" variants for your build, not every possible variants.
    So, shader variants references indirectly by the scenes in you build settings and addressable assets/resources are taken into account. Some additional keywords are also taken into account under the hood (like the normal ones I think).
    Every other variant defined by a keyword only in the build will not exist.

    You can use Shader Variants Collections ( doc here : https://docs.unity3d.com/Manual/shader-variant-collections.html ) to manually specify additional variants in your build.


    Unity has now grown to a company with a few thousand people, it's unrealistic to think that everybody knows about everything done by all the other ones.
     
    chap-unity likes this.
  10. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    848
    Thank you, this is useful. What is the equivalent for compute shaders?

    If all coordination at Unity is relying on everyone talking with everyone in the cantina, then that would explain some of the recent design decisions :p.

    Joke aside, as a user, I do expect features that belong to the core engine to reside in the core engine. With the current scale of Unity, this is of course an organisational problem, and not anyone's fault in particular. @chap-unity's very honest answer "I personally don't have the big picture", is of course valid. Hopefully, someone at Unity has the big picture ... but I would not expect the same people to be capable of making a good decision on whether something like HDMaterial.Validate really should be Material.Validate. You, the developers, have to raise a flag when you see something that could perhaps be relevant to other departments. And of course this only works if there is a efficient organisational structure in place. I rather want one well designed engine with fewer features and fewer updates, than three engines.
     
    Last edited: Dec 24, 2021