Search Unity

One complicated shader or multiple smaller shaders ?

Discussion in 'Shaders' started by xDenny8, Feb 20, 2020.

  1. xDenny8

    xDenny8

    Joined:
    Feb 12, 2017
    Posts:
    18
    Hey guys,

    I just added dissolve shader to my character.. Now Iam wondering if I did the right thing to just replace current URP/Lit shader with Dissolve shader ? or should I increase number of materials on the object to two and add it on top of the old one ? Because I have no idea what I would do, if I would want to add more effects (shaders) to the character (for example electricity shader, invisibility shader etc..). Would it be best practice to stack shaders (materials) on top of each other OR create one big complicated shader with everything inside ?

    Thank you.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Having the default lit shader be the dissolve version all the time probably isn’t a good idea. You do probably want it to be a different shader you swap to. A dissolve shader presumably means there’s a clip() in the fragment shader. Even if it doesn’t change anything (ie: the properties are set so it’s entirely visible) it still changes how the GPU handles that shader and will cause it, and everything rendered after it, to render slower. How much slower depends on the hardware. Desktop GPUs, probably not enough to be too concerned with. Mobile GPUs could be quite significant.
     
    xDenny8 likes this.
  3. xDenny8

    xDenny8

    Joined:
    Feb 12, 2017
    Posts:
    18
    thank you for answer...

    I have made simple shader switcher that is switching to my Dissolve shader only when its needed. Its working nicely so far... only thing is, that I always have to grab texture from original shader and give it to the new shader but I guess its a small price
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If you’re just swapping the shader on the renderer’s material, it shouldn’t loose anything, assuming the texture names are the same for both shaders. The only thing will be if you have a special dissolve texture. That you will need to set at least the first time. You could modify the built in shader to include that texture property to make things easier.

    However swapping the shader on the
    .material
    will create a new material. Every ... single ... time. So swapping the shader to your dissolve shader, new material, switching it back to the original material, new material! What you want to do is cache the materials in your script for each object.

    Before swapping the shader, save a reference to the current
    .sharedMaterial
    , then change the shader using
    .material.shader
    , and then save a reference to
    .sharedMaterial
    again as that’ll now be the new material. From that point on set only the
    .sharedMaterial
    to the two cached material references and never touch
    .material
    again or it may create new materials that can create a memory leak unless you manually destroy the old one each time a new one gets generated.

    However that’s not what I do when I want to do stuff like this. I do something even more complicated. I manually copy the important properties of the original
    .sharedMaterial
    to a
    MaterialPropertyBlock
    , assign that to the renderer, then swap the
    .sharedMaterial
    for a common dissolve material. This means no materials are generated at runtime. Whether or not this is actually more efficient or not ... honestly not sure.
     
    axel_unity305 likes this.
  5. xDenny8

    xDenny8

    Joined:
    Feb 12, 2017
    Posts:
    18
    I guess I am losing the texture because in default shader its called "_BaseMap" while on my Dissolve shader its called "_TEXTURE"

    I am aware that touching .material creates immediately new instance of the material but I wasn't really concerned about it, as I am switching to dissolve shader only for couple of seconds - then the gameobject is destroyed (I assume that new instance of material is destroyed as well).. but I will definitely look at those .sharedMaterial s

    I was also wondering if its actually better to switch shaders rather than entire materials ?? As I am very new to these sort of things, I cant really tell what is good/bad
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    I honestly don’t know if it is.
     
  7. sigil_tech

    sigil_tech

    Joined:
    Sep 18, 2019
    Posts:
    17
    I have a similar situation, I have many shaders I would like to apply to one object using the URP with Shader Graph, I am unsure if I should separate them into different shaders (glow, force field, etc.) or build one large shader and use some kind of conditional statements to decide what is displayed.. What is best practice?
     
  8. HassanKhallouf

    HassanKhallouf

    Joined:
    Mar 5, 2015
    Posts:
    52
    Having the same issue here
    I have multiple effects, blink shader, and dissolve shader and so on, if I do swap, while blink shader is active, I will lose the original colors for the mesh, and if I had one giant shader, controlling it might be a little bit hard, and I will have to expose everything the normal lit shader is exposing already

    so I will ask again if someone knows, what is the best practice ?
     
    dmcdermo36 likes this.
  9. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    If your properties names are the same between the shaders, it should maintain the same texture inputs and color/property values.
    You can also just create keywords in your shader to switch to different render effect modes.
     
  10. HassanKhallouf

    HassanKhallouf

    Joined:
    Mar 5, 2015
    Posts:
    52
    can you please explain the last sentence ? what do you mean by "keyword" ?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html

    And to add to the above documentation, the way you use those in a shader would be something like this:
    Code (CSharp):
    1. // in the Pass’s CGPROGRAM, but not in a function
    2. #pragma shader_feature_local _ DISSOLVE
    3.  
    4. // in the fragment or surf shader function
    5. #if defined(DISSOLVE)
    6. // calculate your dissolve alpha
    7. clip(dissolveAlpha - 0.5);
    8. #endif
    Then from c# you can enable the DISSOLVE keyword on a material to enable that code without it making the default material pay the constant cost of the effect being calculated.
    https://docs.unity3d.com/ScriptReference/Shader.EnableKeyword.html
    Or use a material property drawer to show a toggle for the keyword in the inspector.
    https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html

    The big thing being you must have a unique material to change a keyword, which leads back to my previous post about not creating new materials per game object and instead using material property blocks. Or make sure you manually destroy new materials when the parent object is.
     
    Shushustorm and HassanKhallouf like this.
  12. seoyeon222222

    seoyeon222222

    Joined:
    Nov 18, 2020
    Posts:
    187



    May I ask you in more detail about the generally recommended method?
    For example
    Cards in card game,
    Card basically receives two textures as inputs
    - _CardFrameTex
    - _CardImageTex

    And have many effects.
    - Burn to ashes (like Dissolve)
    - Freeze
    - Holographized
    Special textures may be required for the some Dissolve pattern.

    Method 1)
    One shader, such as "MyCardShader," has all the functions,
    and each function can be controlled through the bool property.

    Method 2)
    Create several shaders for each function.
    - FireCardShader
    - IceCardShader
    - HoloCardShader
    Each shader(material) can also be replaced at runtime.

    If I understand correctly,
    Method 2 is recommended
    And need caching Materials

    Q1) Is this right? Or is there another better way?

    Q2) "However that’s not what I do when I want to do stuff like this. I do something even more complicated"
    If possible,,,, could you share a short code example of your way?
     
    HughRomand likes this.