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. Dismiss Notice

Shader LOD with #define

Discussion in 'Shaders' started by ilya_ca, Dec 7, 2012.

  1. ilya_ca

    ilya_ca

    Joined:
    Nov 19, 2011
    Posts:
    274
    In my shader I have different quality levels, that enable certain features:

    Code (csharp):
    1. #if defined (QUALITY_FASTEST)
    2. #elif defined(QUALITY_FAST)
    3.     #define REFLECTIONS_ON
    4. #elif defined(QUALITY_SIMPLE)
    5.     #define REFLECTIONS_ON
    6.     #define DEPTHMAP_ON
    7. #elif defined(QUALITY_GOOD)
    8.     #define REFLECTIONS_ON
    9.     #define DEPTHMAP_ON
    10.     #define FRESNEL_ON
    11.     #define CALCULATE_NORMALS_ON
    12.     #define NORMALMAP_REFLECTIONS_ON
    13.     #define PERPIXEL_SPECULARITY_ON
    14.     #define DIFFUSE_LIGHTING_ON
    15. #endif
    Is it possible to create different shader LODs with the quality settings? I.e.:
    Code (csharp):
    1. SubShader {
    2.     LOD 200
    3.     Pass {
    4.         CGPROGRAM
    5.         #define QUALITY_FAST
    6.         #include "MyShaderBase.cging"
    7.         ENDCG
    8.     }
    9. }
    10.  
    11. SubShader {
    12.     LOD 100
    13.     Pass {
    14.         CGPROGRAM
    15.         #define QUALITY_FASTEST
    16.         #include "MyShaderBase.cging"
    17.         ENDCG
    18.     }
    19. }
    So basically I want to toggle different parts of my shader, based on the LOD. Can something like this be achieved without the use of keywords?

    Thanks!
     
  2. metaleap

    metaleap

    Joined:
    Oct 3, 2012
    Posts:
    589
    Wondering the same.. did you ever figure out the answer to this? ;)
     
  3. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    This is about the cleanest way you can achieve it:

    Code (csharp):
    1. CGINCLUDE
    2.     #if defined (QUALITY_FASTEST)
    3.     #elif defined(QUALITY_FAST)
    4.         #define REFLECTIONS_ON
    5.     #elif defined(QUALITY_SIMPLE)
    6.         #define REFLECTIONS_ON
    7.         #define DEPTHMAP_ON
    8.     #elif defined(QUALITY_GOOD)
    9.         #define REFLECTIONS_ON
    10.         #define DEPTHMAP_ON
    11.         #define FRESNEL_ON
    12.         #define CALCULATE_NORMALS_ON
    13.         #define NORMALMAP_REFLECTIONS_ON
    14.         #define PERPIXEL_SPECULARITY_ON
    15.         #define DIFFUSE_LIGHTING_ON
    16.     #endif
    17.    
    18.     // Code here...
    19. ENDCG
    20.  
    21. SubShader {
    22.     LOD 200
    23.     Pass {
    24.         CGPROGRAM
    25.         #define QUALITY_FAST
    26.         ENDCG
    27.     }
    28. }
    29.  
    30. SubShader {
    31.     LOD 100
    32.     Pass {
    33.         CGPROGRAM
    34.         #define QUALITY_FASTEST
    35.         ENDCG
    36.     }
    37. }
    Now you can control the quality by simply adjusting the LoD in editor.

    I'm not entirely sure if this works or not, though... If the cginclude block is inserted in front of the define, the keyword won't register. In that case, use what OP posted.
     
    Last edited: Oct 27, 2013
  4. metaleap

    metaleap

    Joined:
    Oct 3, 2012
    Posts:
    589
    Looks good, will give that a try, thanks much!
     
  5. hannes-dev

    hannes-dev

    Joined:
    Apr 27, 2012
    Posts:
    132
    has anyone ever tested this ?

    my shader code always goes inside the subshader
    and unity uses the first subshader it can use

    so how can you use this set up to control your defines without also duplicating all shader code inside all subshader passes
     
  6. hannes-dev

    hannes-dev

    Joined:
    Apr 27, 2012
    Posts:
    132
    i would assume you d have to use a
    UsePass "Shader/Name"
     
  7. hannes-dev

    hannes-dev

    Joined:
    Apr 27, 2012
    Posts:
    132
    ok when putting my code in the CGINCLUDE i can share code between subshaders however toggling the keywords doesnt work.
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,217
    The example using CGINCLUDE won't actually work, but the #include method will. It's an order of operations issue. The order in which preprocessor #define and #if lines exist matter, and the #define needs to happen before the #if otherwise it isn't yet defined. Unfortunately for this case CGINCLUDE is always added at the start of the shader. I've even tried putting the CGINCLUDE at the end of the shader and I don't remember it working then either.

    However using an #include file is also kind of the answer to your other question. You can put almost all of your shader code into the #include and just the quality #define and #pragma lines.
     
    charlesthebird likes this.
  9. hannes-dev

    hannes-dev

    Joined:
    Apr 27, 2012
    Posts:
    132
    how would i include the file ?

    #include "MyShaderBase.cging"

    do i need to provide a path to the cging file?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,217
    The #include for custom cginc files needs to be a relative path. The easiest way to do that is just put the .shader and .cginc file in the same folder so you don't have to worry about it.
     
    Last edited: Feb 13, 2017
    hannes-dev likes this.
  11. hannes-dev

    hannes-dev

    Joined:
    Apr 27, 2012
    Posts:
    132
    seems unity is doing both btw, example from their terrain shader

    they have CGINCLUDE

    and in the cginclude they have a
    #include "TerrainSplatmapCommon.cginc"

    and then they have
    CGPROGRAM
    #pragma target 3.0
    #pragma multi_compile __ _TERRAIN_NORMAL_MAP
    ENDCG

    from what i tried before i would assume this has the same "order of operations issue", however it works.
    Wondering what i am overlooking

    Code (CSharp):
    1.  
    2. ...
    3.     CGINCLUDE
    4.         #pragma surface surf Lambert vertex:SplatmapVert finalcolor:SplatmapFinalColor finalprepass:SplatmapFinalPrepass finalgbuffer:SplatmapFinalGBuffer
    5.         #pragma multi_compile_fog
    6.         #include "TerrainSplatmapCommon.cginc"
    7.  
    8.         void surf(Input IN, inout SurfaceOutput o)
    9.         {
    10.             half4 splat_control;
    11.             half weight;
    12.             fixed4 mixedDiffuse;
    13.             SplatmapMix(IN, splat_control, weight, mixedDiffuse, o.Normal);
    14.             o.Albedo = mixedDiffuse.rgb;
    15.             o.Alpha = weight;
    16.         }
    17.     ENDCG
    18.  
    19.     Category {
    20.         Tags {
    21.             "SplatCount" = "4"
    22.             "Queue" = "Geometry-99"
    23.             "RenderType" = "Opaque"
    24.         }
    25.         // TODO: Seems like "#pragma target 3.0 _TERRAIN_NORMAL_MAP" can't fallback correctly on less capable devices?
    26.         // Use two sub-shaders to simulate different features for different targets and still fallback correctly.
    27.         SubShader { // for sm3.0+ targets
    28.             CGPROGRAM
    29.                 #pragma target 3.0
    30.                 #pragma multi_compile __ _TERRAIN_NORMAL_MAP
    31.             ENDCG
    32.         }
    33. ...
    34.